Reputation:
In this blog, it's shown how to retrieve the metadata of a field.
I'd like to know how to check (other than the above combined with a try-catch-statement) whether a field exist or not.
The reason for that is that when I execute my QueryExpression
, I need to know what columns to include in the ColumnSet
.
The Q&D code right now is this.
private bool DoesFieldExist(String entityName, String fieldName)
{
try
{
RetrieveAttributeRequest req = new RetrieveAttributeRequest();
req.EntityLogicalName = entityName;
req.LogicalName = fieldName;
RetrieveAttributeResponse resp = (RetrieveAttributeResponse)service.Execute(req);
}
catch (Exception) { return false; }
return true;
}
Upvotes: 3
Views: 6315
Reputation: 1499
I know this is a bit late to the game, but I'd like to add a better performing alternative for others looking to do this.
Metadata calls are notoriously slow unless you introduce some kind of caching. If you put a metadata call into a regularly executing plugin you could be asking for trouble. (MS really need to look at why their metadata calls are so slow and fix it!).
If all you simply wanted to do is check the existence of a field I'd force an exception instead. Granted, it doesn't look nice, and some evangelists would frown upon it, but I have found it to execute up to 3 times faster than a metadata call. Which at least puts it in the acceptable bracket for more regularly executing plugins.
This is what I do instead:
try
{
var query = new QueryExpression("account");
query.Criteria.AddCondition("accountid", ConditionOperator.Equal, "294450db-46c9-447e-a642-3babf913d800");
query.NoLock = true;
query.ColumnSet = new ColumnSet("xyz_fieldname");
service.RetrieveMultiple(query);
}
catch
{
// ignored
}
Using a query expression has 2 advantages, you're running it against the id which is a primary key (you don't care about the id, the code will either throw an exception if the field doesn't exist, or return 1 or no records if it succeeds). In short, the purpose isn't to find a record, it's to see if including the column forces an exception.
The second advantage of a query expression is you can run it using a nolock. You really don't care about the result set, the point is forcing an exception.
I have run several tests on CRM online varying between multiple/single calls to the metadata vs multiple/single retrieve multiple calls throwing an exception (using C# stopwatch and reversing code order etc). Exceptions always outperformed the metadata. If you're performing multiple calls within the same code it jumps to about 3 times faster (I'm guessing optimizations come into play). Either way, the bottle neck is the call to the Metadata service which cannot be optimized until you introduce caching and more code complexity. Also, if the field exists you won't get an exception which means yet another performance bonus.
The only scenario I haven't tested is running it against a massively heavily used entity... but if you're hitting your database so hard that a nolock retrieve cannot return in an acceptable timeframe you probably have bigger problems to worry about!
Upvotes: 2
Reputation: 39058
private bool DoesFieldExist(String entityName, String fieldName)
{
RetrieveEntityRequest request = new RetrieveEntityRequest
{
EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.Attributes,
LogicalName = entityName
};
RetrieveEntityResponse response
= (RetrieveEntityResponse)service.Execute(request);
return response.EntityMetadata.Attributes.FirstOrDefault(element
=> element.LogicalName == fieldName) != null;
}
The EntityFilters
requires that you add using Microsoft.Xrm.Sdk.Metadata;
in order to work properly if you dislike the explicit reference in my example (and you should dislike it because it's ugly).
I'd prefer to use FirstOrDefault
instead of SingleOrDefault
. While in this case it can't give you any problems (the attribute either is there or not), in other cases you may get an exception if there are multiple elements satisfying the condition (should you look for a match over multiple attributes or do something else that can cause that).
Upvotes: 5
Reputation: 10344
Another possibility is to load the EntityMetadata for the entity.
var request = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.Attributes,
LogicalName = entityName
};
var response = (RetrieveEntityResponse)_serviceProxy.Execute(request);
You could cache this in memory or on disk for performance reasons. In order to check whether an attribute is defined you could no access the attributes property
var defined = response.EntityMetadata
.Attributes
.SingleOrDefault(a => a.LogicalName == fieldName) != null;
Upvotes: 3