OdataClient SaveChanges doesn't work and results an empty Microsoft.OData.Client.DataServiceResponse - asp.net-web-api

I encapsulated the odata container because of some extensions:
public class Connector
{
public string apiKey{get;set;}
public Connector(string apiKey)
{
this.apiKey = apiKey;
}
public Default.Container Get()
{
Default.Container container = new Default.Container(new Uri("http://documents.omnisoftonline.be/odata"));
container.BuildingRequest += container_BuildingRequest;
return container;
}
/// <summary>
/// When fetching a single Document, you can enable this method to including fetch the binary data
/// </summary>
/// <param name="container"></param>
/// <returns></returns>
internal bool doIO = false;
internal void container_BuildingRequest(object sender, BuildingRequestEventArgs e)
{
e.Headers.Add("X-ApiKey", apiKey);
if (doIO && e.RequestUri.ToString().Contains("/Documents"))
{
e.RequestUri = new Uri(e.RequestUri.ToString() + (e.RequestUri.Query.Contains("?") ? "&" : "?") + "doIO=true");
}
this.container_BuildingRequest(sender, e);
}
}
When i use my .dll ( using the Connector class), i have an empty result ( statuscode = -1, no headers, ...)
This is how i call the DLL
documentsConnector.Get().AddToDocuments(docToCreate);
var serviceResponse = documentsConnector.Get().SaveChanges(Microsoft.OData.Client.SaveChangesOptions.None); //tried several options
foreach(var operationResponse in serviceResponse)
{
Console.WriteLine("Response: {0}", operationResponse.StatusCode); //no operationResponses, so this isn't executed
}
It could be because my object isn't valid. But it's weird that i don't see any validation happening...
Any thoughts on how to propagate the SaveChanges() or pre-validate ( before submit) the Entity? The post isn't happening ( checked with Fiddler)

My wrapper class created a new container every time, so the entities got deleted from the Default.Container

Related

Delay in handling message in NServiceBus

This is not about Delayed message delivery. Lately I've noticed my endpoint has a delay in handling a message when looking at when the message is taken from the RabbitMQ queue.
I was wondering if there is some configuration option I’m overlooking that has an impact on the delay between the receipt of the message from RabbitMQ by NServiceBus and the actual handling of the message (command) in the IHandleMessages<> handler.
I’m seeing a consistent delay when comparing the NServiceBus logfile and the logfile of the handler. It does vary a bit between client and server but in a given session it’s always constant. Now I’m running through all the options that I can find but there’s no real pointer for me out in the wild.
The thing is I’m running a workflow solution that uses NServiceBus to do all the messaging between the workflow server (based on MS WF) and 50/60 clients. The server runs on NServiceBus 7.6.0 but all client still use NServiceBus 5.2.26. I decided to write a small test client that also runs on NServiceBus 7.6.0 but that also shows the delay albeit a smaller one. But the delay between individual commands is constant at 6.5 seconds in the test client. So the message is retrieved by NSB from the queue and 6.5 seconds the IHandleMessages<> handler is invoked.
I’ve tried a couple of things (direct routing vs conventional routing, making sure license files where distributed properly for example). The only thing I’m able to come up right now is flat out making a new test server of the workflow server I’m using that is only able to accept 1 command, generating a reply and nothing more and see if that makes a difference.
Configuration wise the solution runs on a Windows Server 2016 having 4 cores, RabbitMQ 3.7.21 and NServiceBus 7.6.0. Clients run on Windows 10 machines using NServiceBus 5.2.26 but the test client uses NServiceBus 7.6.0.
Now for the logs. This is the NServicebus log when it receives the GetVersionCommand:
2022-01-20 16:09:58.104 DEBUG Processing message type: HyFlo.Service.Messages.Commands.Info.GetVersionCommand
Message headers:
CorrelationId : 619d678d-25bf-46c0-b5c0-57c624e3d557
NServiceBus.MessageId : 548813d5-2465-4ff5-904d-ae2300f9ee23
NServiceBus.MessageIntent : Send
NServiceBus.ConversationId : 367575b0-7bee-40d0-90ca-ae2300f9ee24
NServiceBus.CorrelationId : 548813d5-2465-4ff5-904d-ae2300f9ee23
NServiceBus.TimeToBeReceived : 00:10:00
NServiceBus.ReplyToAddress : Hyflo.Client.Test
NServiceBus.OriginatingMachine : DYP297
NServiceBus.OriginatingEndpoint : Hyflo.Client.Test
$.diagnostics.originating.hostid : 3a3bbd6661214b655aa6dbcd95d112a2
NServiceBus.ContentType : text/xml
NServiceBus.EnclosedMessageTypes : HyFlo.Service.Messages.Commands.Info.GetVersionCommand, HyFlo.Service.Messages, Version=4.10.3.26734, Culture=neutral, PublicKeyToken=null;HyFlo.Types.Interfaces.IInfoCommand, HyFlo.Types, Version=4.10.8.20782, Culture=neutral, PublicKeyToken=null
NServiceBus.Version : 7.6.0
NServiceBus.TimeSent : 2022-01-20 15:09:58:107461 Z
NServiceBus.NonDurableMessage : False
Handlers to invoke:
HyFlo.Service.CommandHandlers.Info.GetVersionHandler
Then the custom log of the GetVersionHandler:
2022-01-20 16:10:04.6516 TRACE = New Message =====================================================================================================================================
2022-01-20 16:10:04.6516 TRACE CommandEventId:9125b036-c315-484e-9948-889ed1e56587: New message handled by handler of type 'HyFlo.Service.CommandHandlers.Info.GetVersionHandler' with CorrelationId '619d678d-25bf-46c0-b5c0-57c624e3d557' ..
2022-01-20 16:10:04.6516 TRACE ===================================================================================================================================================
2022-01-20 16:10:04.6516 INFO Cache contains a systemstatus ..
2022-01-20 16:10:04.6516 INFO Retrieved systemstatus from cache is: 0
2022-01-20 16:10:04.6516 INFO Running the injected code ..
2022-01-20 16:10:04.6672 TRACE ===================================================================================================================================================
2022-01-20 16:10:04.6672 TRACE CommandEventId:9125b036-c315-484e-9948-889ed1e56587: Message processed by handler of type 'HyFlo.Service.CommandHandlers.Info.GetVersionHandler' with CorrelationId '619d678d-25bf-46c0-b5c0-57c624e3d557' ..
2022-01-20 16:10:04.6672 TRACE Elapsed time: 00:00:00.0156159 ..
2022-01-20 16:10:04.6672 TRACE = End of Message ==================================================================================================================================
The command is received at 2022-01-20 16:09:58.104 but is delivered at the Handler at 2022-01-20 16:10:04.6516 and this delay is quite consistent between this and other commands. I just can't get my head around why this delay is there.
The endpoint configuration is the following:
internal class ProgramService : ServiceBase
{
private IEndpointInstance _endpointInstance = null;
/// <summary>
/// Determines if application is ran as service or just as console application
/// </summary>
/// <param name="name"></param>
/// <returns>true if it's a service</returns>
private static bool IsService(string name)
{
if (!Environment.UserInteractive) return true;
var sc = new ServiceController(name);
try
{
return sc.Status == ServiceControllerStatus.StartPending;
}
catch (InvalidOperationException)
{
return false;
}
}
/// <summary>
/// Main entry
/// </summary>
private static void Main()
{
using (var service = new ProgramService())
{
// so we can run interactive from Visual Studio or as a windows service
if (!IsService("HyFlo.Service"))
{
Console.CancelKeyPress += (sender, e) => { service.OnStop(); };
service.OnStart(null);
Console.WriteLine("\r\nPress enter key to stop program\r\n");
Console.Read();
service.OnStop();
return;
}
Run(service);
}
}
/// <summary>
/// On critical errors bail out
/// </summary>
/// <param name="errorMessage"></param>
/// <param name="exception"></param>
async Task OnCriticalError(ICriticalErrorContext context)
{
var fatalMessage = $"The following critical error was encountered:\n{context.Error}\nProcess is shutting down.";
try
{
Console.WriteLine("fatalMessage: " + fatalMessage);
TraceWriter.Error(fatalMessage);
if (context.Exception.InnerException != null)
{
TraceWriter.Error("innerException message: " + context.Exception.InnerException.Message + #"\n" +
context.Exception.InnerException.StackTrace);
}
await context.Stop().ConfigureAwait(false);
}
finally
{
Environment.FailFast(fatalMessage, context.Exception);
}
}
/// <summary>
/// Starting the service
/// </summary>
/// <param name="args"></param>
private async Task AsyncOnStart()
{
TraceWriter.Trace("AsyncOnStart() running .. ");
try
{
TraceWriter.Info("Running configuration management ..");
var config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var section =
config.GetSection("connectionStrings");
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection("DPAPIProtection");
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Modified);
}
var endpointConfig = new EndpointConfiguration("hyflo.service");
endpointConfig.EnableInstallers();
endpointConfig.SendFailedMessagesTo("hyflo.service.errors");
endpointConfig.AuditProcessedMessagesTo("hyflo.service.audit");
endpointConfig.UseSerialization<XmlSerializer>();
endpointConfig.DefineCriticalErrorAction(OnCriticalError);
endpointConfig.LimitMessageProcessingConcurrencyTo(8);
var persistenceMethod = ConfigurationManager.AppSettings["NServicebusPersistence"];
if (persistenceMethod.ToUpper() == "RAVENDB")
{
string connectionString = ConfigurationManager.ConnectionStrings["NServiceBus/Persistence"].ConnectionString;
TraceWriter.Info($"Setting persistence to RavenDBPersistence based on connectionString '{connectionString}' .. ");
var documentStore = new Raven.Client.Document.DocumentStore
{
ConnectionStringName = "NServiceBus/Persistence",
DefaultDatabase = "HyFlo.Service"
};
documentStore.Initialize();
endpointConfig.UsePersistence<RavenDBPersistence>().SetDefaultDocumentStore(documentStore);
}
else
{
TraceWriter.Info("Setting persistence to InMemoryPersistence .. ");
endpointConfig.UsePersistence<InMemoryPersistence>();
}
var transportConnection = ConfigurationManager.ConnectionStrings[#"NServiceBus/Transport"];
string transportConnectionString = transportConnection.ConnectionString;
if (String.IsNullOrEmpty(transportConnectionString))
{
transportConnectionString = "host=localhost";
}
TraceWriter.Info($"Configuring RabbitMQTransport for connection '{transportConnectionString}' .. ");
var transport = endpointConfig.UseTransport<RabbitMQTransport>();
transport.ConnectionString(transportConnectionString);
transport.UseConventionalRoutingTopology();
string databusBasePath = ConfigurationManager.AppSettings["DataBusBasePath"] ?? "";
TraceWriter.Info($"Setting Databus's basepath to '{databusBasePath}' ..");
endpointConfig.UseDataBus<FileShareDataBus>().BasePath(databusBasePath);
TraceWriter.Info("Scannning for Hyflo assemblies .. ");
List<string> hyfloAssemblies =
Directory.GetFiles(Directory.GetCurrentDirectory(), "HyFlo.*dll", SearchOption.TopDirectoryOnly).ToList();
TraceWriter.Info("Initializing Autofac with assemblies .. ");
foreach (string assemblyName in hyfloAssemblies)
{
TraceWriter.Info($"Scanning '{assemblyName}' for an Autofac module .. ");
}
try
{
var containerSettings = endpointConfig.UseContainer(new AutofacServiceProviderFactory());
containerSettings.ConfigureContainer(containerBuilder =>
{
var loadedAssemblies = hyfloAssemblies.Select(Assembly.LoadFile).ToList();
containerBuilder.RegisterAssemblyModules(loadedAssemblies.ToArray());
});
}
catch (Exception ex)
{
TraceWriter.Error($"{ex.Message}\n{ex.StackTrace}\n{ex.InnerException?.Source}");
await OnCriticalError(new CriticalErrorContext(null, $"Exception occurred during initialization. Exception is: {ex.Message}\n{ex.StackTrace}\n{ex.InnerException?.Source}", ex));
}
TraceWriter.Info("Setting up default message conventions ..");
var conventions = endpointConfig.Conventions();
conventions.DefiningTimeToBeReceivedAs(type => TimeSpan.FromMinutes(10));
var logFactory = LogManager.Use<DefaultFactory>();
logFactory.Level(LogLevel.Debug);
endpointConfig.EnableFeature<HyfloInitializationFeature>();
TraceWriter.Info("Initialized! Now starting Hyflo ..");
_endpointInstance = await Endpoint.Start(endpointConfig);
TraceWriter.Info("Hyflo started ..");
}
catch (Exception exception)
{
TraceWriter.Error($"{exception.Message}\n{exception.StackTrace}\n{exception.InnerException?.Source}");
await OnCriticalError(new CriticalErrorContext(null, "Failed to start the bus.", exception));
}
}
/// <summary>
/// Stopping the service
/// </summary>
/// <returns></returns>
private Task AsyncOnStop()
{
TraceWriter.Info("Shutting down Hyflo ..");
if (_endpointInstance != null)
{
return _endpointInstance.Stop();
}
return Task.CompletedTask;
}
// <summary>
/// Starting the service
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
TraceWriter.Trace("ProgramService.OnStart() ..");
AsyncOnStart().GetAwaiter().GetResult();
}
/// <summary>
/// Stopping the service
/// </summary>
protected override void OnStop()
{
TraceWriter.Trace("ProgramService.OnStop() ..");
AsyncOnStop().GetAwaiter().GetResult();
}
}
The HyfloInitializationFeature is a startup task that sends a list of all current workflows to the connected clients.
using HyFlo.Tasks.Interfaces;
using NServiceBus.Features;
namespace HyFlo.Service
{
public class HyfloInitializationFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
context.RegisterStartupTask(c => new SystemStartupTask(c.Build<IGetInfoOnAllWorkflowsTask>(), c.Build<ISystemTestTask>()));
}
}
}
This is the SystemStartupTask:
namespace HyFlo.Service
{
public class SystemStartupTask : FeatureStartupTask, IDisposable
{
private readonly IGetInfoOnAllWorkflowsTask _getInfoOnAllWorkflowsTask;
private readonly ISystemTestTask _systemTestTask;
public SystemStartupTask(IGetInfoOnAllWorkflowsTask getInfoOnAllWorkflowsTask, ISystemTestTask systemTestTask)
{
_getInfoOnAllWorkflowsTask = getInfoOnAllWorkflowsTask;
_systemTestTask = systemTestTask;
}
public void Dispose()
{
}
/// <summary>
/// OnStart
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected override async Task OnStart(IMessageSession session)
{
TraceWriter.Trace($"==> Running SystemStartupTask.OnStart() ..");
DeleteHeartbeatIndicator();
ObjectCache cache = MemoryCache.Default;
var policy = new CacheItemPolicy { SlidingExpiration = (TimeSpan.FromSeconds(15)) };
bool systemOk = false;
if (_systemTestTask != null)
{
_systemTestTask.ContextWrapper = new MessageContextWrapper { Session = session };
_systemTestTask.QuickTest = false;
_systemTestTask.TestComponentsMask = 31;
_systemTestTask.SystemStartup = true;
if (_systemTestTask.Run())
{
var systemResultTexts = new List<string>();
if (int.TryParse(_systemTestTask.Results.ToString(), out int systemsResult))
{
if ((systemsResult & 1) == 1)
{
systemResultTexts.Add("HSB offline");
}
if ((systemsResult & 2) == 2)
{
systemResultTexts.Add("HDM offline");
}
if ((systemsResult & 4) == 4)
{
systemResultTexts.Add("SqlServer offline");
}
if ((systemsResult & 8) == 8)
{
systemResultTexts.Add("Workflow Queueing offline");
}
}
TraceWriter.Trace(
$"SystemStartupTask: Results returned by systemtest: '{(!systemResultTexts.Any() ? "All online" : String.Join(",", systemResultTexts))}' ..");
if (!_systemTestTask.Failure && !systemResultTexts.Any())
{
TraceWriter.Info("HyFlo dependencies all up and running ..");
systemOk = true;
// for caching purposes
if (_getInfoOnAllWorkflowsTask != null)
{
_getInfoOnAllWorkflowsTask.UserId = "";
_getInfoOnAllWorkflowsTask.Run();
}
}
else
{
TraceWriter.Warn("HyFlo can't be started. One or more of its dependencies returned a failure!");
}
}
}
else
{
TraceWriter.Warn("A general failure occurred during the Hyflo systemcheck!");
}
var allworkflowStates = new WorkflowState[0];
if (_getInfoOnAllWorkflowsTask != null)
{
if (!_getInfoOnAllWorkflowsTask.Failure && _getInfoOnAllWorkflowsTask.Results is List<WorkflowState>)
{
allworkflowStates = (_getInfoOnAllWorkflowsTask.Results as List<WorkflowState>).ToArray();
}
TraceWriter.Info(
$"Workflowstate retrieval completed. Failure: '{_getInfoOnAllWorkflowsTask.Failure}', Results: '{allworkflowStates.Count()}' workflow{(allworkflowStates.Count() == 1 ? "" : "s")} found ..");
var timeStamp = DateTime.Now;
TraceWriter.Trace("Sending IStartedHyflo # GUI with the following parameters:\n" +
$"\tErrorCode: '{Convert.ToInt32(_systemTestTask?.Results)}'\n" +
$"\tFailure: '{!systemOk}'\n" + $"\tTimeStamp: '{timeStamp}'\n" +
$"\tResulting workflowcount: '{allworkflowStates.Length}'\n");
await session.Publish<IStartedHyflo>(evt =>
{
evt.ErrorCode = Convert.ToInt32(_systemTestTask?.Results);
evt.Failure = !systemOk;
evt.TimeStamp = timeStamp;
evt.Result = new DataBusProperty<WorkflowState[]>(allworkflowStates);
}).ConfigureAwait(false);
cache.Set("SystemTest", systemOk, policy);
}
}
/// <summary>
/// OnStop
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected override async Task OnStop(IMessageSession session)
{
TraceWriter.Trace($"==> Running SystemStartupTask.OnStop() ..");
TraceWriter.Info("Deleting heartbeat indicator .. ");
DeleteHeartbeatIndicator();
var timeStamp = DateTime.Now;
TraceWriter.Trace("Sending IShutdownHyflo # GUI with the following parameters:\n" + "\tErrorCode: '0'\n" +
"\tFailure: 'false'\n" + $"\tTimeStamp: '{timeStamp}'");
await session.Publish<IShutdownHyflo>(evt =>
{
evt.ErrorCode = 0;
evt.Failure = false;
evt.TimeStamp = timeStamp;
}).ConfigureAwait(false);
TraceWriter.Info("HyFlo has shutted down .. ");
}
/// <summary>
/// Function deletes the heartbeat.txt file that indicates wether a heartbeat flow is running or not
/// </summary>
private void DeleteHeartbeatIndicator()
{
string stateFolder = ConfigurationManager.AppSettings["StateFolder"];
string fullStateFolder = stateFolder.IndexOf(":", StringComparison.Ordinal) == -1
? $"{AppDomain.CurrentDomain.BaseDirectory}{stateFolder}"
: stateFolder;
string fileName = $#"{fullStateFolder}\heartbeat.txt";
if (File.Exists(fileName))
{
File.Delete(fileName);
}
}
}
}
The GetVersionHandler is the following:
namespace HyFlo.Service.CommandHandlers.Info
{
public class GetVersionHandler : HandlerBase, IHandleMessages<GetVersionCommand>
{
public GetVersionHandler(ISystemTestTask systemTestTask, ITaskBuilderController taskBuilderController)
{
_systemTestTask = systemTestTask;
_taskBuilderController = taskBuilderController;
}
public virtual async Task Handle(GetVersionCommand message, IMessageHandlerContext context)
{
if (message == null)
return;
await RunDirect(async () =>
{
var assemblyVersion = Assembly.GetAssembly(typeof(GetVersionHandler)).GetName().Version;
var reply = new GetVersionReply
{
Failure = false,
Version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}",
TimeStamp = DateTime.Now,
TaskId = CommandEventId,
ErrorCode = 0
};
await Retry.ExecuteAsync(async () =>
{
var replyOptions = new ReplyOptions();
replyOptions.SetHeader("CorrelationId", CorrelationId);
await context.Reply(reply, replyOptions);
});
}, new MessageContextWrapper { HandlerContext = context });
}
}
}
The RunDirect method is located in the HandlerBase class and one of the first things it does is log that a new message is being handled.
public async Task RunDirect(Action codeToRun, IMessageContextWrapper contextWrapper)
{
_taskBuilderController.HandlerBase = this;
CommandEventId = Guid.NewGuid();
ContextWrapper = contextWrapper;
CorrelationId = GetHeaderValue("CorrelationId");
string fullMethodName = GetType().ToString();
TraceWriter.Trace("= New Message =====================================================================================================================================");
TraceWriter.Trace($"CommandEventId:{CommandEventId}: New message handled by handler of type '{fullMethodName}' with CorrelationId '{CorrelationId}' .. ");
TraceWriter.Trace("===================================================================================================================================================");
...
The last thing is the test client I've used to send this GetVersionCommand:
namespace HPS_EndpointTest
{
internal class Program
{
static void Main(string[] args)
{
var endpointConfiguration = new EndpointConfiguration("Hyflo.Client.Test");
endpointConfiguration.EnableInstallers();
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.PurgeOnStartup(true);
var transport = endpointConfiguration.UseTransport<RabbitMQTransport>();
endpointConfiguration.UseTransport<RabbitMQTransport>().ConnectionString("host=hswv0601;username=hyflo;password=hyflo");
endpointConfiguration.UseTransport<RabbitMQTransport>().UseDirectRoutingTopology();
IEndpointInstance endpointInstance = null;
AsyncPump.Run(async delegate
{
endpointInstance = await Endpoint.Start(endpointConfiguration).ConfigureAwait(false);
});
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": Client started ..");
var correlationId = Guid.NewGuid();
string destination = "hyflo.service";
var sendOptions = new SendOptions();
sendOptions.SetHeader("CorrelationId", correlationId.ToString());
sendOptions.SetDestination(destination);
sendOptions.RequireImmediateDispatch();
var versionMessage = new HyFlo.Service.Messages.Commands.Info.GetVersionCommand();
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": Sending message ..");
AsyncPump.Run(async delegate
{
if (endpointInstance != null)
{
await endpointInstance.Send(versionMessage, sendOptions).ConfigureAwait(false);
}
});
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": Message sent!");
while (true)
{
var key = Console.ReadKey();
if (key.Key == ConsoleKey.Enter)
{
break;
}
}
AsyncPump.Run(async delegate
{
await endpointInstance.Stop().ConfigureAwait(false);
});
}
}
public class RetrieveAllWorkflowsHandler : IHandleMessages<HyFlo.Service.Messages.Events.IRetrievedAllWorkflowsEvent>
{
public Task Handle(HyFlo.Service.Messages.Events.IRetrievedAllWorkflowsEvent message, IMessageHandlerContext context)
{
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": IRetrievedAllWorkflowEvents reply received ..");
return Task.CompletedTask;
}
}
public class RetrieveVersionHandler : IHandleMessages<GetVersionReply>
{
public Task Handle(GetVersionReply message, IMessageHandlerContext context)
{
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": GetVersionReply reply received ..");
return Task.CompletedTask;
}
}
}
I just don't understand why this delay is there.
What I'm doing right now is create a completely new endpoint with limited functionality (ie only supporting the GetVersionCommand) and see what happens with that. If the delay isn't there I'll expand support with other commands and see what happens then. Hope any of you guys sees this and slaps him/herself to the head and replying "but you forgot so and so".
Thanks in advance!!
With help of Daniel Marbach I managed to pin point the problem. Because of a complex object graph in one of the classes of the solution (the class that is the primary access point to the solution in fact) the delay occurred. Now when creating either specialised classes or injecting the actual needed dependency there was virtually no delay.
So the final solution was to alter each and every IHandleMessages-handler to have it injected a targeted dependency with which the handler could work.

Kentico 10 Filtered Repeater with multiple filters not working properly

I have a data source, filter, and a filtered repeater that I have created custom filters for.
For some reason, both my filters work without the other on the screen. One works fine with the other filter on the screen.
The last filter refuses to work when I have the other filter on the screen but works fine without anything else.
I am pretty sure it has something to do with my code behind files which I will put below.
FILTER#1
using CMS.DocumentEngine;
using CMS.Helpers;
using System;
using System.Web;
using System.Web.UI.WebControls;
using CMS.DocumentEngine.Web.UI;
public partial class CMSGlobalFiles_SectorFilterControl : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Sets up the inner child controls.
/// </summary>
private void SetupControl()
{
// Hides the filter if StopProcessing is enabled
if (this.StopProcessing)
{
this.Visible = false;
}
// Initializes only if the current request is NOT a postback
else if (!RequestHelper.IsPostBack())
{
// Loads product departments as filtering options
InitializeClientSectors();
}
}
/// <summary>
/// Loads all existing product departments as filtering options into the department drop-down list.
/// </summary>
private void InitializeClientSectors()
{
// Adds the default '(all)' option
this.drpSector.Items.Insert(0, new ListItem("(all)", "##ALL##"));
var clientSectors = DocumentHelper.GetDocuments("BBUS.Sector")
.Path("/Sector/", PathTypeEnum.Children)
.OnSite("Balfour-dev.allata.com");
if (!DataHelper.DataSourceIsEmpty(clientSectors))
{
int count = 1;
foreach (var clientSector in clientSectors)
{
var ClientSectorID = clientSector.GetValue("SectorID").ToString();
this.drpSector.Items.Insert(count++, new ListItem(clientSector.DocumentName, ClientSectorID));
}
}
}
/// <summary>
/// Generates a WHERE condition and ORDER BY clause based on the current filtering selection.
/// </summary>
private void SetFilter()
{
string where = null;
// Generates a WHERE condition based on the selected product department
if (this.drpSector.SelectedIndex > 0 && this.drpSector.SelectedValue != null)
{
//where = string.Format("clientSector = {0}", this.drpClientSector.SelectedValue);
where = string.Format(
"sector LIKE '%|{0}|%' " +
"OR sector LIKE '{0}|%' " +
"OR sector LIKE '%|{0}' " +
"OR sector = '{0}'", this.drpSector.SelectedValue);
}
if (where != null)
{
// Sets the Where condition
this.WhereCondition = where;
}
// Raises the filter changed event
this.RaiseOnFilterChanged();
}
/// <summary>
/// Init event handler.
/// </summary>
protected override void OnInit(EventArgs e)
{
// Creates the child controls
SetupControl();
base.OnInit(e);
}
/// <summary>
/// PreRender event handler
/// </summary>
protected override void OnPreRender(EventArgs e)
{
var ClientSectorID = HttpContext.Current.Request.QueryString.Get("SectorID");
// Checks if the current request is a postback
if (RequestHelper.IsPostBack())
{
// Applies the filter to the displayed data
SetFilter();
}
else if (!string.IsNullOrEmpty(ClientSectorID))
{
this.drpSector.SelectedIndex = this.drpSector.Items.IndexOf(this.drpSector.Items.FindByValue(ClientSectorID));
SetFilter();
}
base.OnPreRender(e);
}
protected void btnFilter_Click(object sender, EventArgs e)
{
// Remove Query Strings
string url = Request.RawUrl.Split(new[] { '?' })[0];
// Add ClientSectorID Query String
string updatedQueryString = "?" + "SectorID=" + this.drpSector.SelectedValue;
Response.Redirect(url + updatedQueryString);
}
}
FILTER#2
using CMS.DocumentEngine;
using CMS.Helpers;
using System;
using System.Web;
using System.Web.UI.WebControls;
using CMS.DocumentEngine.Web.UI;
public partial class CMSGlobalFiles_SectorFilterControl : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Sets up the inner child controls.
/// </summary>
private void SetupControl()
{
// Hides the filter if StopProcessing is enabled
if (this.StopProcessing)
{
this.Visible = false;
}
// Initializes only if the current request is NOT a postback
else if (!RequestHelper.IsPostBack())
{
// Loads product departments as filtering options
InitializeClientSectors();
}
}
/// <summary>
/// Loads all existing product departments as filtering options into the department drop-down list.
/// </summary>
private void InitializeClientSectors()
{
// Adds the default '(all)' option
this.drpSector.Items.Insert(0, new ListItem("(all)", "##ALL##"));
var clientSectors = DocumentHelper.GetDocuments("BBUS.Sector")
.Path("/Sector/", PathTypeEnum.Children)
.OnSite("Balfour-dev.allata.com");
if (!DataHelper.DataSourceIsEmpty(clientSectors))
{
int count = 1;
foreach (var clientSector in clientSectors)
{
var ClientSectorID = clientSector.GetValue("SectorID").ToString();
this.drpSector.Items.Insert(count++, new ListItem(clientSector.DocumentName, ClientSectorID));
}
}
}
/// <summary>
/// Generates a WHERE condition and ORDER BY clause based on the current filtering selection.
/// </summary>
private void SetFilter()
{
string where = null;
// Generates a WHERE condition based on the selected product department
if (this.drpSector.SelectedIndex > 0 && this.drpSector.SelectedValue != null)
{
//where = string.Format("clientSector = {0}", this.drpClientSector.SelectedValue);
where = string.Format(
"sector LIKE '%|{0}|%' " +
"OR sector LIKE '{0}|%' " +
"OR sector LIKE '%|{0}' " +
"OR sector = '{0}'", this.drpSector.SelectedValue);
}
if (where != null)
{
// Sets the Where condition
this.WhereCondition = where;
}
// Raises the filter changed event
this.RaiseOnFilterChanged();
}
/// <summary>
/// Init event handler.
/// </summary>
protected override void OnInit(EventArgs e)
{
// Creates the child controls
SetupControl();
base.OnInit(e);
}
/// <summary>
/// PreRender event handler
/// </summary>
protected override void OnPreRender(EventArgs e)
{
var ClientSectorID = HttpContext.Current.Request.QueryString.Get("SectorID");
// Checks if the current request is a postback
if (RequestHelper.IsPostBack())
{
// Applies the filter to the displayed data
SetFilter();
}
else if (!string.IsNullOrEmpty(ClientSectorID))
{
this.drpSector.SelectedIndex = this.drpSector.Items.IndexOf(this.drpSector.Items.FindByValue(ClientSectorID));
SetFilter();
}
base.OnPreRender(e);
}
protected void btnFilter_Click(object sender, EventArgs e)
{
// Remove Query Strings
string url = Request.RawUrl.Split(new[] { '?' })[0];
// Add ClientSectorID Query String
string updatedQueryString = "?" + "SectorID=" + this.drpSector.SelectedValue;
Response.Redirect(url + updatedQueryString);
}
}
Sadly this is a little bit of a limitation, you can only have 1 filter per Repeater/Data Source (except Smart Search it seems, which can handle multiple).
You will most likely need to combine both of your filters into 1 filter, and combine the logics into one where condition.
https://docs.kentico.com/k10/developing-websites/loading-and-displaying-data-on-websites/filtering-and-paging-data
Would be great to allow multiple filters though!

Json.Net TypeNameHandling.Auto and Asp Web Api Controller gives unexpected behaviour

Assume TypeNameHandling.Auto is used for Json.net in the following Web Api Controllers:
class A {}
class B : A {}
class FooController : ApiController
{
public A Get() {
return new A();
}
}
class BarController : ApiController
{
public A Get() {
return new B();
}
}
Then I would expect the resulting Json to be:
Foo
{}
Bar
{'$type':...}
However, the output of Bar is also {}. On the other hand, if the API controller returned IEnumerable<A> and we returned a lot of B's then the type property is set.
Is it possible to change this behaviour such that it uses the return type as input to Json.Net?
A fix is to return the Json and not the object but I find that a dissatisfactory solution.
Thanks for this scenario as I think this is something which should be included by default in our Json formatter. Following is a custom Json formatter where I try to pass in the 'type' information to the Serialize method of JsonSerializer. I tried the below custom formatter with your scenario and it seemed to work fine.
(Most of the code below was cherry picked from existing Web API source code to suit your scenario.)
public class CustomJsonFormatter : JsonMediaTypeFormatter
{
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
try
{
Encoding effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers);
if (!UseDataContractJsonSerializer)
{
using (JsonTextWriter jsonTextWriter = new JsonTextWriter(new StreamWriter(writeStream, effectiveEncoding)) { CloseOutput = false })
{
if (Indent)
{
jsonTextWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
}
JsonSerializer jsonSerializer = JsonSerializer.Create(this.SerializerSettings);
jsonSerializer.Serialize(jsonTextWriter, value, type); //NOTE: passing in 'type' here
jsonTextWriter.Flush();
}
}
else
{
return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
}
return TaskHelpers.Completed();
}
catch (Exception e)
{
return TaskHelpers.FromError(e);
}
}
}
internal class TaskHelpers
{
private static readonly Task _defaultCompleted = FromResult<AsyncVoid>(default(AsyncVoid));
/// <summary>
/// Used as the T in a "conversion" of a Task into a Task{T}
/// </summary>
private struct AsyncVoid
{
}
internal static Task<TResult> FromResult<TResult>(TResult result)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetResult(result);
return tcs.Task;
}
/// <summary>
/// Returns an error task. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
internal static Task FromError(Exception exception)
{
return FromError<AsyncVoid>(exception);
}
/// <summary>
/// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
/// <typeparam name="TResult"></typeparam>
internal static Task<TResult> FromError<TResult>(Exception exception)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetException(exception);
return tcs.Task;
}
/// <summary>
/// Returns a completed task that has no result.
/// </summary>
internal static Task Completed()
{
return _defaultCompleted;
}
}
I had to change Kiran's solution with the following such that arrays etc. will be written as [...] instead of {$type: IEnumerab...., values = ...}.
var contract = jsonSerializer.ContractResolver.ResolveContract(type);
// Only use the declared type if it is an object contract such that
// arrays, dictionaries, etc. aren't affected.
if (contract is JsonObjectContract)
{
jsonSerializer.Serialize(jsonTextWriter, value, type); // NOTE: passing in 'type' here
}
else
{
jsonSerializer.Serialize(jsonTextWriter, value);
}

How can I make CRMSvcUtil.exe generate unduplicated, error-free early-bound option sets?

I use Erik Pool's implementation of ICodeWriterFilterService and Manny Grewal's GenerateOption function as a model to filter out unwanted entities in the file that CRMSvcUtil generates. While Erik recommends returning true for the GenerateOptionSet method to generate enums for option sets, doing so duplicates any of the global option sets that are used by any particular entity (as mentioned in one of the comments on that post).
To address this, I check to see if the option set has been already generated, and if so, I return the default option (presumably false for most cases) as in the below.
//list of generated option sets, instantiated in the constructor
private List<string> GeneratedOptionSets;
public bool GenerateOptionSet
(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
{
if (!GeneratedOptionSets.Contains(optionSetMetadata.Name))
{
GeneratedOptionSets.Add(optionSetMetadata.Name);
return true;
}
return _defaultService.GenerateOptionSet(optionSetMetadata, services);
}
But when incorporating the generated file in my CRM projects, the compilation error
Cannot convert type 'Microsoft.Xrm.Sdk.OptionSetValue' to 'int'
is always thrown by every line of code that looks like
this.SetAttributeValue
("address1_shippingmethodcode", new Microsoft.Xrm.Sdk.OptionSetValue(((int)(value))));
.
As a workaround, I use a separate project where I filter the entities I need, run CRMSvcUtil with the arguments Erik suggests, replace the troublesome part of the code (int)(value) (where value is an OptionSetValue) with value.Value after the file is generated, and then resave the file, and all issues go away.
My question is this: do I need to do something differently that will fix this compilation error with the default CRMSvcUtil generated file without doing something so hackish as altering that generated file?
You can use the ICustomizeCodeDomService interface to rewrite the SetAttributeValue method for the optionSets. Snippet below:
namespace The.NameSpace
{
using System;
using System.CodeDom;
using System.Diagnostics;
using System.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
/// <summary>
/// The customize code dom service.
/// </summary>
public sealed class CustomizeCodeDomService : ICustomizeCodeDomService
{
#region Constants and Fields
/// <summary>
/// The metadata.
/// </summary>
private IOrganizationMetadata metadata;
#endregion
#region Properties
#endregion
#region Public Methods
/// <summary>
/// The customize code dom.
/// </summary>
/// <param name="codeCompileUnit">
/// The code compile unit.
/// </param>
/// <param name="services">
/// The services.
/// </param>
public void CustomizeCodeDom(CodeCompileUnit codeCompileUnit, IServiceProvider services)
{
// Locate the namespace to use
CodeNamespace codeNamespace = codeCompileUnit.Namespaces[0];
var metadataProviderService = (IMetadataProviderService)services.GetService(typeof(IMetadataProviderService));
var filterService = (ICodeWriterFilterService)services.GetService(typeof(ICodeWriterFilterService));
this.metadata = metadataProviderService.LoadMetadata();
foreach (EntityMetadata entityMetadata in this.metadata.Entities)
{
if (filterService.GenerateEntity(entityMetadata, services))
{
CodeTypeDeclaration entityClass =
codeNamespace.Types.Cast<CodeTypeDeclaration>().First(codeType => codeType.Name.ToUpper() == entityMetadata.SchemaName.ToUpper());
UpdateEnumSetter(entityClass, entityMetadata);
}
}
}
#endregion
#region Private Methods
private static void UpdateEnumSetter(
CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf)))
{
//Match the respective field Name.
AttributeMetadata metadata1 = attributeMetadata;
foreach (
CodeTypeMember codeMembers in
entityClass.Members.Cast<CodeTypeMember>().Where(
codeMembers => codeMembers.Name == metadata1.SchemaName))
{
var codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet)
{
if (attributeMetadata.AttributeType != null && attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist)
{
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] =
new CodeSnippetStatement
{
Value =
String.Format(
"this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));",
attributeMetadata.LogicalName)
};
Debug.WriteLine(String.Format("{0}.{1}", entity.LogicalName, attributeMetadata.LogicalName));
}
}
}
}
}
#endregion
}
}
Some changes to the UpdateEnumSetter method:
private static void UpdateEnumSetter(CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf)))
{
AttributeMetadata currentMetadata = attributeMetadata;
foreach (CodeTypeMember codeMembers in entityClass.Members.Cast<CodeTypeMember>().Where(codeMembers => codeMembers.Name == currentMetadata.SchemaName))
{
CodeMemberProperty codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet)
{
if (attributeMetadata.AttributeType != null && (attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist || attributeMetadata.AttributeType.Value == AttributeTypeCode.Status))
{
if (codeProperty.SetStatements[1].GetType() == typeof(CodeConditionStatement))
{
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] = new CodeSnippetStatement
{
Value = String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName)
};
}
else
{
codeProperty.SetStatements[1] = new CodeSnippetStatement(String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName));
}
}
}
}
}
}
I'm betting that Guarav's answer is the real way to go, but in the absence of documentation surrounding CRMSvcUtil, I'm forced to use my workaround. (I use a separate project where I filter the entities I need, run CRMSvcUtil with the arguments Erik suggests, replace the troublesome part of the code(int)(value) (where value is an OptionSetValue) with value.Value after the file is generated, and then resave the file.)
Not a perfect solution, but it's been working on the few samples I've worked with so far.
It turns out that this fault is to do with the code attempting to make optionsets that look like the code below when the types are available for use. Note the only difference is the correct type being chose for the return type and the cast.
It should be possible to update the codegen stuff to fix this bug, but it might be better to get microsoft to fix the damn thing properly, I would make a solution but I don't really have time to implement it right now because we have a mostly working solution even if we have to deal with the optionsetvalue class.
public enum entityname_optionsetname
{
Value = 200
}
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("myprefix_fieldname")]
public entityname_optionsetname myprefix_FieldName
{
get
{
Microsoft.Xrm.Sdk.OptionSetValue optionSet = this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("myprefix_fieldname");
if ((optionSet != null))
{
return ((entityname_optionsetname)(System.Enum.ToObject(typeof(Microsoft.Xrm.Sdk.OptionSetValue), optionSet.Value)));
}
else
{
return null;
}
}
set
{
this.OnPropertyChanging("myprefix_FieldName");
if ((value == null))
{
this.SetAttributeValue("myprefix_fieldname", null);
}
else
{
this.SetAttributeValue("myprefix_fieldname", new Microsoft.Xrm.Sdk.OptionSetValue(((int)(value))));
}
this.OnPropertyChanged("myprefix_FieldName");
}
}
I finally am able to generate early bound class with a filtered set of entities and error free option set. I found the bulk of my answer through this thread, so thanks guys. The problem though is it's difficult to compile all of the various suggestions into something that actually... compiles. So I thought I'd post my final solution for the benefit of others, here's what worked for me.
I used Erik Pool's, Manny Grewal's, and Peter Majeed's solution for outputting only distinct enums with proper values, then combined that with Gaurav Dalal's solution (updated by JFK007 to fix the cast error) to re-write the SetAttributeValue that caused the (int)(value) error. And as an added bonus, I used the same solution for filtering distinct option sets to also filter for distinct option set values (which was an issue in my org).
The result is a class library containing CodeWriterFilter and CustomizeCodeDomService, the cmd batch file to run CrmSvcUtil.exe, and the filter.xml to filter the entities.
In your class library add references to CrmSvcUtil.exe, Microsoft.Xrm.Sdk, and System.Runtime.Serialization then compile the dll and copy it to the same the folder as your CrmSvcUtil.exe. Use the command I've included to reference your new assembly and build the early bound class file.
CodeWriterFilter:
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
using System.Text.RegularExpressions;
using Microsoft.Xrm.Sdk;
namespace SvcUtilFilter
{
/// <summary>
/// CodeWriterFilter for CrmSvcUtil that reads list of entities from an xml file to
/// determine whether or not the entity class should be generated.
/// </summary>
public class CodeWriterFilter : ICodeWriterFilterService
{
//list of entity names to generate classes for.
private HashSet<string> _validEntities = new HashSet<string>();
//reference to the default service.
private ICodeWriterFilterService _defaultService = null;
//list of generated option sets, instantiated in the constructor
private List<string> GeneratedOptionSets;
//list of generated options, instantiated in the constructor
private List<string> GeneratedOptions;
/// <summary>
/// constructor
/// </summary>
/// <param name="defaultService">default implementation</param>
public CodeWriterFilter(ICodeWriterFilterService defaultService)
{
this._defaultService = defaultService;
this.GeneratedOptionSets = new List<string>();
this.GeneratedOptions = new List<string>();
LoadFilterData();
}
/// <summary>
/// loads the entity filter data from the filter.xml file
/// </summary>
private void LoadFilterData()
{
XElement xml = XElement.Load("filter.xml");
XElement entitiesElement = xml.Element("entities");
foreach (XElement entityElement in entitiesElement.Elements("entity")) {
_validEntities.Add(entityElement.Value.ToLowerInvariant());
}
}
/// <summary>
/// /Use filter entity list to determine if the entity class should be generated.
/// </summary>
public bool GenerateEntity(EntityMetadata entityMetadata, IServiceProvider services)
{
return (_validEntities.Contains(entityMetadata.LogicalName.ToLowerInvariant()));
}
//All other methods just use default implementation:
public bool GenerateAttribute(AttributeMetadata attributeMetadata, IServiceProvider services)
{
return _defaultService.GenerateAttribute(attributeMetadata, services);
}
public bool GenerateOption(OptionMetadata optionMetadata, IServiceProvider services)
{
//return _defaultService.GenerateOption(optionMetadata, services);
string label = optionMetadata.Label.UserLocalizedLabel.Label;
//remove spaces and special characters
label = Regex.Replace(label, #"[^a-zA-Z0-9]", string.Empty);
if (label.Length > 0 && !char.IsLetter(label, 0)) {
label = "Number_" + label;
}
else if (label.Length == 0) {
label = "empty";
}
if (!GeneratedOptions.Exists(l=>l.Equals(label))) {
GeneratedOptions.Add(label);
optionMetadata.Label = new Label(label, 1033);
return _defaultService.GenerateOption(optionMetadata, services);
}
else { return false; }
}
public bool GenerateOptionSet(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
{
//return _defaultService.GenerateOptionSet(optionSetMetadata, services);
if (!GeneratedOptionSets.Contains(optionSetMetadata.Name)) {
GeneratedOptionSets.Add(optionSetMetadata.Name);
return true;
}
return _defaultService.GenerateOptionSet(optionSetMetadata, services);
}
public bool GenerateRelationship(RelationshipMetadataBase relationshipMetadata, EntityMetadata otherEntityMetadata, IServiceProvider services)
{
return _defaultService.GenerateRelationship(relationshipMetadata, otherEntityMetadata, services);
}
public bool GenerateServiceContext(IServiceProvider services)
{
return _defaultService.GenerateServiceContext(services);
}
}
}
CustomizeCodeDomService:
using System;
using System.CodeDom;
using System.Diagnostics;
using System.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
namespace SvcUtilFilter
{
/// <summary>
/// The customize code dom service.
/// </summary>
public sealed class CustomizeCodeDomService : ICustomizeCodeDomService
{
#region Constants and Fields
/// <summary>
/// The metadata.
/// </summary>
private IOrganizationMetadata metadata;
#endregion
#region Properties
#endregion
#region Public Methods
/// <summary>
/// The customize code dom.
/// </summary>
/// <param name="codeCompileUnit">
/// The code compile unit.
/// </param>
/// <param name="services">
/// The services.
/// </param>
public void CustomizeCodeDom(CodeCompileUnit codeCompileUnit, IServiceProvider services)
{
// Locate the namespace to use
CodeNamespace codeNamespace = codeCompileUnit.Namespaces[0];
var metadataProviderService = (IMetadataProviderService)services.GetService(typeof(IMetadataProviderService));
var filterService = (ICodeWriterFilterService)services.GetService(typeof(ICodeWriterFilterService));
this.metadata = metadataProviderService.LoadMetadata();
foreach (EntityMetadata entityMetadata in this.metadata.Entities) {
if (filterService.GenerateEntity(entityMetadata, services)) {
CodeTypeDeclaration entityClass =
codeNamespace.Types.Cast<CodeTypeDeclaration>().First(codeType => codeType.Name.ToUpper() == entityMetadata.SchemaName.ToUpper());
UpdateEnumSetter(entityClass, entityMetadata);
}
}
}
#endregion
#region Private Methods
private static void UpdateEnumSetter(CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf))) {
AttributeMetadata currentMetadata = attributeMetadata;
foreach (CodeTypeMember codeMembers in entityClass.Members.Cast<CodeTypeMember>().Where(codeMembers => codeMembers.Name == currentMetadata.SchemaName)) {
CodeMemberProperty codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet) {
if (attributeMetadata.AttributeType != null && (attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist || attributeMetadata.AttributeType.Value == AttributeTypeCode.Status)) {
if (codeProperty.SetStatements[1].GetType() == typeof(CodeConditionStatement)) {
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] = new CodeSnippetStatement {
Value = String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName)
};
}
else {
codeProperty.SetStatements[1] = new CodeSnippetStatement(String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName));
}
}
}
}
}
}
#endregion
}
}
CrmSvcUtil_run.cmd Command Batch File:
#echo off
set url=https://[organization].api.crm.dynamics.com/XRMServices/2011/Organization.svc
echo.
echo Generating CrmSvcUtil Proxy class in output folder
echo.
CrmSvcUtil.exe /metadataproviderservice:"MetadataProvider.IfdMetadataProviderService,
MetadataProvider"
/url:https://[organization].api.crm.dynamics.com/XRMServices/2011/Organization.svc /out:Xrm.cs
/namespace:Xrm /serviceContextName:XrmServiceContext /serviceContextPrefix:Xrm
/u:[username] /p:[password]
/codewriterfilter:SvcUtilFilter.CodeWriterFilter,SvcUtilFilter
/codecustomization:SvcUtilFilter.CustomizeCodeDomService,SvcUtilFilter
echo.
pause
filter.xml
<filter>
<entities>
<entity>systemuser</entity>
<entity>team</entity>
<entity>role</entity>
<entity>businessunit</entity>
<entity>account</entity>
<entity>product</entity>
<entity>transactioncurrency</entity>
</entities>
</filter>
It looks like there was a bug in the crmsrvcutil that has since been fixed. My code for OptionSet properties now looks like this:
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("prioritycode")]
public Microsoft.Xrm.Sdk.OptionSetValue PriorityCode
{
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("prioritycode");
}
set
{
this.OnPropertyChanging("PriorityCode");
this.SetAttributeValue("prioritycode", value);
this.OnPropertyChanged("PriorityCode");
}
}
And I get no error setting the OptionSetValue...

Update SubmitChanges() - does not update

With this code there is no change in my database.
when the above code is run there is no longer a new entry created neither is an entry updated.
public void UpdateCallback(callback cb_)
{
callback call = context.callbacks.Single(c => c.callbackID == cb_.callbackID);
//call.callbackID = cb_.callbackID;
call.status = cb_.status;
call.contactName = cb_.contactName;
call.company = cb_.company;
call.phone = cb_.phone;
call.calledDate = cb_.calledDate;
call.callback1 = cb_.callback1;
call.notes = cb_.notes;
try
{
context.SubmitChanges();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
This post is similar to yours.
In his case, the update did not work because the table did not have a primary key.
Have you verified that CallbackId is defined as a PK in the database?
Nothing immediate jumps out at me. I have found it useful to use the Log property of the DataContext to see the SQL being generated.
See http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.log.aspx
You could then use something like the code below to output the SQL to the Visual Studio Debug window while debugging.
/// <summary>
/// Implementation of a <see cref="TextWriter"/> that outputs to the debug window
/// </summary>
public class DebugTextWriter : TextWriter
{
public override void Write(char[] buffer, int index, int count)
{
System.Diagnostics.Debug.Write(new string(buffer, index, count));
}
public override void Write(string value)
{
System.Diagnostics.Debug.Write(value);
}
public override Encoding Encoding
{
get { return System.Text.Encoding.Default; }
}
}

Resources