Hi I'm developing CRM 2011 Plugin and I have a problem with reflected type. I have generated Entities classes and I know that property exists in the type but when I try to get its value I'm getting exception about method not being found. The stupidest part is that it works perfectly on my machine but doesn't work on clients.
Here's my code (I need to take all OptionSets from the entity and perform action on them):
public override void MyExecute()
{
var fse = TargetEntity.ToEntity<Equipment>();
Type equiptmentType = fse.GetType();
TracingService.Trace("FSE object type: {0}", equiptmentType.FullName);
IEnumerable<PropertyInfo> optionSetsProperties = equiptmentType.GetProperties()
.Where(x => x.PropertyType == typeof(OptionSetValue)).ToList(); //I'm getting this property from the object type so it must exist.
foreach (var optionSetsProperty in optionSetsProperties)
{
TracingService.Trace("Resolving ResourceGroup on property: {0}", optionSetsProperty.Name);
ResolveResourceGroupBySkill(optionSetsProperty, fse);
TracingService.Trace("Resoloving ResourceGroup finished");
}
}
private void ResolveResourceGroupBySkill(PropertyInfo optionSetsProperty, Equipment fse)
{
try
{
TracingService.Trace("Trying to get value of: {0}", optionSetsProperty.Name);
OptionSetValue skillLevel = (OptionSetValue)optionSetsProperty.GetValue(fse);
TracingService.Trace("Value equals: {0}", skillLevel);
}
catch (Exception ex)
{
TracingService.Trace("An error occured: {0}", ex.Message);
Exception inner = ex;
while (inner != null)
{
TracingService.Trace(inner.Message);
inner = inner.InnerException;
}
throw new InvalidOperationException(String.Format("Cannot get value of skill level from property: {0}", optionSetsProperty.Name), ex);
}
}
Here's the Log details:
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Method not found: 'System.Object System.Reflection.PropertyInfo.GetValue(System.Object)'.Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ErrorCode>-2147220891</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<KeyValuePairOfstringanyType>
<d2p1:key>OperationStatus</d2p1:key>
<d2p1:value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">0</d2p1:value>
</KeyValuePairOfstringanyType>
</ErrorDetails>
<Message>Method not found: 'System.Object System.Reflection.PropertyInfo.GetValue(System.Object)'.</Message>
<Timestamp>2014-09-11T12:58:09.2941554Z</Timestamp>
<InnerFault i:nil="true" />
<TraceText>
[OC.CSSFieldService: OC.CSSFieldService.ServiceActivity.MyPlugin]
[424ad2a7-ea29-e411-be7f-00155d0aa109: OC.CSSFieldService.ServiceActivity.MyPlugin: Create of equipment]
FSE object type: OC.Data.Equipment
Resolving ResourceGroup on property: oc_ExpeCommHyper
</TraceText>
</OrganizationServiceFault>
As you can see even the tracing line "Trying to get value of" is not working. The exception is not caught... I don't know what to do. Any thoughts?
Ok I figured it out. The server has Microsft .NET 4.0 installed and I have .NET 4.5.
In the .NET 4.5 there's a new overload for PropertyInfo.GetValue method - it's PropertyInfo.GetValue(object obj) since in 4.0 there is only PropertyInfo.GetValue(object obj, object[] indexer)
I just had to replace:
OptionSetValue skillLevel = (OptionSetValue)optionSetsProperty.GetValue(fse);
with
OptionSetValue skillLevel = (OptionSetValue)optionSetsProperty.GetValue(fse, null);
worked like a charm!
Related
When trying to deserialize an observablecollection, it gives me an exception
**"
"An exception of type 'System.NotSupportedException' occurred in protobuf-net.Core.dll but was not handled in user code
Additional information: A repeated type was not expected as an aux type:""**
public Task<T> ReceiveDataAsync<T>(TcpClient client)
{
using (NetworkStream stream = new NetworkStream(client.Client, false))
{
return Task.FromResult(Serializer.DeserializeWithLengthPrefix<T>(stream, PrefixStyle.Fixed32));
}
}
I haven't found details about this exception at all.
The WithLengthPrefix API expects a single message, not a collection. Maybe serialize something that has a collection.
I've started using the MongoDB .Net driver to connect a WPF application to a MongoDB database hosted on MongoLabs.
But the following method I created to load the connection(called on the MainViewModel's constructor), threw a timeout exception on the line marked in the method below.
I tried to resolve the error further by adding an exception check of type MongoException to no avail. Also checked that the connection string is valid as per the docs and it seems so: (password starred out for security)
private const string connectionString = "mongodb://<brianVarley>:<********>#ds048878.mongolab.com:48878/orders";
The specific error thrown is as follows:
An exception of type 'System.TimeoutException' occurred in mscorlib.dll
Complete Error Link: http://hastebin.com/funanodufa.tex
Does anyone know the reason why I'm getting the timeout on my connection method?
public List<Customer> LoadCustomers()
{
var client = new MongoClient(connectionString);
var database = client.GetDatabase("orders");
//Get a handle on the customers collection:
var collection = database.GetCollection<Customer>("customers");
try
{
//Timeout error thrown at this line:
customers = collection.Find(new BsonDocument()).ToListAsync().GetAwaiter().GetResult();
}
catch(MongoException ex)
{
//Log exception here:
MessageBox.Show("A handled exception just occurred: " + ex.Message, "Connection Exception", MessageBoxButton.OK, MessageBoxImage.Warning);
}
return customers;
}
Solved this error by re-editing my connection string. I had left these two symbols in my connection string in error, '<' and '>' between the user name and password credentials.
Correct format:
"mongodb://brianVarley:password#ds054118.mongolab.com:54118/orders";
Incorrect format:
"mongodb://<brianVarley>:<password;>#ds054118.mongolab.com:54118/orders";
I'm developing with C#, ASP.NET MVC Web Api, Entity Framework and .NET Framework 4.0.
I have this code to log an exception:
public void LogCompleteException(
string controllerName,
string actionName,
Exception exception)
{
string exceptionMessage = string.Empty;
Exception e = exception;
if (e.InnerException == null)
e = null;
else
while (e.InnerException != null) e = e.InnerException;
if (e == null)
exceptionMessage = exception.Message;
else
exceptionMessage = string.Format("{0}\n\rInnerException: {1}", exception.Message, e.Message);
_logger.ErrorFormat(
LogTextFormatString,
ExceptionText,
controllerName,
actionName,
exceptionMessage);
}
But on my log file I have found this:
Validation failed for one or more entities. See
'EntityValidationErrors' property for more details.
When I wrote 'See 'EntityValidationErrors' property for more details.', I'm only showing an example where I haven't log an important property.
There are a lot of kind of exceptions; but with my method I'm not logging all the relevant information because there could be properties like 'EntityValidationErrors' that I don't log.
When I pass the exception to log it I don't know what properties it has, and I don't know how to log each property it has.
Do you know a method to log an exception completely? My code doesn't long exceptions with an EntityValidationErrors property or any other important property.
I'm doing the logging with log4net.
Since the inner exception is an exception itself, perhaps you can just recurse and reuse the method you already have:
if (e.InnerException != null)
{
LogCompleteException(controllerName, actionName, e.InnerException);
}
If this does work, however, you will still be missing the EntityValidationErrors.
Another method, which I used recently is to just explicitly trap and log the exception where it occurs:
try
{
db.Add(something);
db.SaveChanges();
}
catch (DbEntityValidationException ex)
{
StringBuilder sb = new StringBuilder();
// collect some extra info about the exception before logging
foreach (var eve in ex.EntityValidationErrors)
{
sb.AppendLine(String.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", eve.Entry.Entity.GetType().Name, eve.Entry.State));
foreach (var ve in eve.ValidationErrors)
{
sb.AppendLine(String.Format("Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage));
}
}
logger.Error("There was an error while trying to parse and save something!\n" + sb.ToString(), ex);
}
If you want the Exception.Data dictionary entries, you can add those as well:
foreach (DictionaryEntry entry in ex.Data)
{
// you will want to use a StringBuilder instead of concatenating strings if you do this
exceptionMessage = exceptionMessage + string.Format("Exception.Data[{0}]: {1}", entry.Key, entry.Value);
}
As for properties for Custom exception classes just as the EntityValidationErrors, I would just trap and parse those where they occur. Otherwise you would have to override ToString() on every exception type or use some reflection hackery to enumerate all the properties which would significantly clutter the logs with properties you dont care about.
you could use elmah and the log4net appender. Elmah logs catches all exceptions and can log them to a log4net instance.
I have a common method that I'm using to handle a specific error that may come back from a number of functions:
protected async Task<T> RunMyMethod<T>(Func<T> method)
{
try
{
var returnValue = await Task.Run<T>(method);
return returnValue;
}
catch (MyCustomException)
{
// Force a clean shutdown of the software
ShutdownApplication();
return default(T);
}
}
Here's an example of how that is then used in a derived class:
private async Task<IEnumerable<MyData>> GetMyData()
{
var returnValue = await base.RunMyMethod<IEnumerable<MyData>>(() =>
{
var returnval = GetMyDataFromServer();
return returnval;
});
return returnValue;
}
When an exception of type MyCustomException occurs in GetMyDataFromServer() the software doesn't drop into the catch block. I get the following error in the function GetMyData():
An exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll but was not handled in user code
Additional information: Exception of type 'MyCustomException' was thrown.
This is with only User-unhandled exceptions turned on.
GetMyDataFromServer() communicates with a WCF service. This service is what throws the error.
ChannelFactory<TChannel> cf = new ChannelFactory<TChannel>(endPointName);
Binding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
var clientCredentials = new ClientCredentials();
. . .
channel = cf.CreateChannel();
var data = channel.CallWCFService();
Having looked around on-line, it appeared that the correct way to handle this was to change the base method as follows:
protected async Task<T> RunMyMethod<T>(Func<T> method)
{
var returnValue = await Task.Run<T>(method).ContinueWith(e =>
{
ShutdownApplication();
return default(T);
}, TaskContinuationOptions.OnlyOnFaulted);
return returnValue;
}
When I run this, I'm obviously not trapping for the correct error message, but I'm just getting a TaskCancellationException.
So, I have two questions: is my conclusion about how to handle this exception correct and, if so, how do I trap a specific error; and why am I getting a TaskCancellationException?
You get TaskCancellationException because the continuation is cancelled as it's conditional (i.e. TaskContinuationOptions.OnlyOnFaulted) and the condition isn't met since the antecedent task wasn't faulted.
There's no reason to use that method of adding a continuation. Using async-await like you did at the start is good enough (and even simpler).
The issue is that you are trying to catch MyCustomException but that isn't the exception being thrown. Since you're using WCF the exception is FaultException. You can check the "real" exception stored in FaultException.InnerException.
I've written a plugin with the following configuration:
I'm simply trying to set one datetime field to equal another datetime field:
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parmameters.
Entity entity = (Entity)context.InputParameters["Target"];
try
{
if (entity.LogicalName == "list" && entity.Attributes["gbs_lastusedonoriginal"] != null)
{
entity.Attributes["lastusedon"] = entity.Attributes["gbs_lastusedonoriginal"];
service.Update(entity);
}
}
catch (FaultException ex)
{
throw new InvalidPluginExecutionException("An error occured in the plug-in.", ex);
}
}
The exception I get is:
Unhandled Exception:
System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault,
Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35]]: An error occured in the
plug-in.Detail:
-2147220891
OperationStatus
0
SubErrorCode
-2146233088
An error occured in the plug-in.
2015-01-15T05:34:00.1772929Z
[PreValidationMarketingList.Plugins:
PreValidationMarketingList.Plugins.PreValidateMarketingListCreate]
[5454a088-749c-e411-b3df-6c3be5a83130: PreValidateMarketingListCreate]
Entered
PreValidationMarketingList.Plugins.PreValidateMarketingListCreate.Execute(),
Correlation Id: 6d3ed105-f9c4-4006-9c80-08abd97c0140, Initiating User:
5e1b0493-d07b-e411-b592-f0921c199288
PreValidationMarketingList.Plugins.PreValidateMarketingListCreate is
firing for Entity: list, Message: Create, Correlation Id:
6d3ed105-f9c4-4006-9c80-08abd97c0140, Initiating User:
5e1b0493-d07b-e411-b592-f0921c199288 Exiting
PreValidationMarketingList.Plugins.PreValidateMarketingListCreate.Execute(),
Correlation Id: 6d3ed105-f9c4-4006-9c80-08abd97c0140, Initiating User:
5e1b0493-d07b-e411-b592-f0921c199288
What am I doing wrong?In crm 2013 how do I set one field to equal another field if both of them are datetimes?
You shouldn't be calling Update in this plugin because you haven't created and saved the record you are trying to update.
First, move this to pre-operation, not pre-validation. It's a nit but pre-op is really the appropriate place since setting lastusedon is not required for validation on Create of list.
I've reworked your code to do some additional checks:
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parmameters.
Entity entity = (Entity)context.InputParameters["Target"];
try
{
if (entity.LogicalName == "list" && entity.Attributes.Contains("gbs_lastusedonoriginal") && entity["gbs_lastusedonoriginal"] != null)
{
if (entity.Attributes.Contains("lastusedon") )
entity.Attributes["lastusedon"] = entity.Attributes["gbs_lastusedonoriginal"];
else entity.Attributes.Add("lastusedon", entity.Attributes["gbs_lastusedonoriginal"];
}
}
catch (FaultException ex)
{
throw new InvalidPluginExecutionException("An error occured in the plug-in.", ex);
}
}
Check this link on MSDN. In the Pre-Event, the record is not yet saved in the SQL database. You can modify the Entity object from the InputParameters. After the pre-event plugin the record will be created with your modified attributes.
The main difference between Pre-validation and Pre-operation is that the Pre-operation stage is executed within the database transaction while Pre-validation not. See MSDN for more information.