Set CRM Lookup Values with WebAPI - dynamics-crm-2016

Who had done CRM Web API calls to update CRM entities with Lookup values from another Entity.
I'm trying to set a Lookup Value to another Entity within CRM using WebAPI, CRM 2016. It works if I disable the Lookup value but once I enable the Lookup value, I receive Bad Request.
Below is my code in LinqPad so it does work.
void Main()
{
using(var webClient = new WebClient()){
webClient.Credentials = new NetworkCredential("Username", "Password", "Domain");
webClient.Headers.Add("OData-MaxVersion", "4.0");
webClient.Headers.Add("OData-Version", "4.0");
webClient.Headers.Add("accept", "application/json");
webClient.Headers.Add("Content-Type","application/json");
webClient.Headers.Add("Prefer", "odata.include-annotations=*");
webClient.BaseAddress = "http://dev.company.com/DEV2016/api/data/v8.0/";
var JO = new JObject();
JO.Add("col_name","My Name");
//JO.Add("col_contactid#odata.bind","/contacts(7266f26b-7105-e611-811e-005056b61789)");
var dataString = JO.ToString();
var responseString = webClient.UploadString("col_advisors", "POST", dataString);
Console.WriteLine(webClient.ResponseHeaders.Get("OData-EntityId"));
}
}

Case matters with the WebAPI. Make sure your col_contactid is the schema name, not the logical name. For example, the logical name of your attribute is col_contactid (logical names are always lowercase), but schema names often times have upper case letters. Yours might be col_ContactId for example, in which case you would want to use col_ContactId#odata.bind.
The easiest way to find the schema name of your attribute is by going to CRM -> Settings -> Solutions -> your solution -> Entites (on the left) -> Advisors -> Fields. In that grid you'll see a column for schema name.

I got it to work. The fields really have to be unique as it is case sensitive. Comments here and also this blog, really helped.
http://inogic.com/blog/2016/02/set-values-of-all-data-types-using-web-api-in-dynamics-crm/
Step 1 : Goto Cutomization  Developer Resource.
Step 2 : Click to “Download Odata Metadata” link and Download the same.
Step 3 : Once Download, open it and find out name of lookup attribute ( i.e. new_qualifiedleadid) and check its casing.
Step 4 : Verify it with the value which you are setting in the code it should be same.
While my column was col_contactid, CRM renames the Navigational Column to be what was above col_ContactId.
I also used Postman(google chrome) plugin and added the following Header to my Post.
webClient.Headers.Add("Prefer", "odata.include-annotations=*");

Related

Dynamics 365 implementation auto number with plugin

I have already implement auto number with plugin, and it is through another entity type to ensure all operations are in one transaction.
but I have another question, that is can we use process lock(eg. mutex) to fix it? this will more flexible than before, isn't it?
Dynamics 365 has native support for auto-numbering fields since v9.0, with the minor inconvenience of having to manipulate them through code only.
Unless it's a broken feature (it happens), there's no need for custom autonumbers anymore.
https://learn.microsoft.com/en-us/dynamics365/customer-engagement/developer/create-auto-number-attributes
Example:
CreateAttributeRequest widgetSerialNumberAttributeRequest = new CreateAttributeRequest
{
EntityName = "newWidget",
Attribute = new StringAttributeMetadata
{
//Define the format of the attribute
AutoNumberFormat = "WID-{SEQNUM:5}-{RANDSTRING:6}-{DATETIMEUTC:yyyyMMddhhmmss}",
LogicalName = "new_serialnumber",
SchemaName = "new_SerialNumber",
RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
MaxLength = 100, // The MaxLength defined for the string attribute must be greater than the length of the AutoNumberFormat value, that is, it should be able to fit in the generated value.
DisplayName = new Label("Serial Number", 1033),
Description = new Label("Serial Number of the widget.", 1033)
}
};
_serviceProxy.Execute(widgetSerialNumberAttributeRequest);
Docs point out that
The sequential segment is generated by SQL and hence uniqueness is guaranteed by SQL.
XrmToolbox should have a plugin to manage auto number fields (thus making it easier), but I haven't tried it.
Plugin's can be run on multiple machines concurrently depending on your installation - that's why the entity (database) lock is regularly used.

Plugin performance in Microsoft Dynamics CRM 2013/2015

Time to leave the shy mode behind and make my first post on stackoverflow.
After doing loads of research (plugins, performance, indexes, types of update, friends) and after trying several approaches I was unable to find a proper answer/solution.
So if possible I would like to get your feedback/help in a Microsoft Dynamics CRM 2013/2015 plugin performance issue (or coding technique)
Scenario:
Microsoft Dynamics CRM 2013/2015
2 Entities with Relationship 1:N
EntityA
EntityB
EntityB has the following columns:
Id | EntityAId | ColumnDemoX (decimal) | ColumnDemoY (currency)
Entity A has: 500 records
Entity B has: 150 records per each Entity A record. So 500*150 = 75000 records.
Objective:
Create a Post Entity A Plugin Update to "mimic" the following SQL command
Update EntityB
Set ColumnDemoX = (some quantity), ColumnDemoY = (some quantity) * (some value)
Where EntityAId = (some id)
One approach could be:
using (var serviceContext = new XrmServiceContext(service))
{
var query = from a in serviceContext.EntityASet
where a.EntityAId.Equals(someId)
select a;
foreach (EntityA entA in query)
{
entA.ColumnDemoX = (some quantity);
serviceContext.UpdateObject(entA);
}
serviceContext.SaveChanges();
}
Problem:
The foreach for 150 records in the post plugin update will take 20 secs or more.
While the
Update EntityB Set ColumnDemoX = (some quantity), ColumnDemoY = (some quantity) * (some value) Where EntityAId = (some id)
it will take 0.00001 secs
Any suggestion/solution?
Thank you all for reading.
H
You can use the ExecuteMultipleRequest, when you iterate the 150 entities, save the entities you need to update and after that call the request. If you do this, you only call the service once, that's very good for the perfomance.
If your process could be bigger and bigger, then you should think making it asynchronous as a plug-in or a custom activity workflow.
This is an example:
// Create an ExecuteMultipleRequest object.
requestWithResults = new ExecuteMultipleRequest()
{
// Assign settings that define execution behavior: continue on error, return responses.
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = false,
ReturnResponses = true
},
// Create an empty organization request collection.
Requests = new OrganizationRequestCollection()
};
// Add a UpdateRequest for each entity to the request collection.
foreach (var entity in input.Entities)
{
UpdateRequest updateRequest = new UpdateRequest { Target = entity };
requestWithResults.Requests.Add(updateRequest);
}
// Execute all the requests in the request collection using a single web method call.
ExecuteMultipleResponse responseWithResults =
(ExecuteMultipleResponse)_serviceProxy.Execute(requestWithResults);
Few solutions comes to mind but I don't think they will please you...
Is this really a problem ? Yes it's slow and database update can be so much faster. However if you can have it as a background process (asynchronous), you'll have your numbers anyway. Is it really a "I need this numbers in the next second as soon as I click or business will go down" situation ?
It can be a reason to ditch 2013. In CRM 2015 you can use a calculated field. If you need this numbers only to show up in forms (eg. you don't use them in reporting), you could also do it in javascript.
Warning this is for the desesperate call. If you really need your update to be synchronous, immediate, you can't use calculated fields, you really know what your doing etc... Why not do it directly in the database? I know this is a very bad advice. There are a lot of reason not to do it this way (you can read a few here). It's unsupported and if you do something wrong it could go really bad. But if your real situation is as simple as your example (just a calculated field, no entity creation, no relation modification), you could do it this way. You'll have to consider many things: you won't have any audit on the fields, no security, caching issues, no modified by, etc. Actually I pretty much advise against this solution.
1 - Put it this logic to async workflow.
OR
2 - Don't use
serviceContext.UpdateObject(entA);
serviceContext.SaveChanges();.
Get all the records (150) from post stage update the fields and ExecuteMultipleRequest to update crm records in one time.
Don't send update request for each and every record

Attribute routing not working with dictionaries

Being new to attribute routing, I'd like to ask for help getting this to work.
This test is a simple dynamic DB table viewer: Given a table name (or stored query name or whatever) and optionally some WHERE parameters, return query results.
Table COMPANIES (one of any number of tables which has an associated SELECT query stored somewhere, keyed by table name):
ID NAME HQ INDUSTRY
1 Apple USA Consumer electronics
2 Bose USA Low-quality, expensive audio equipment
3 Nokia FIN Mobile Phones
Controller:
[Route("view/{table}/{parameters}")]
public object Get(string table, Dictionary<string, string> parameters) {
var sql = GetSql(table);
var dbArgs = new DynamicParameters(parameters);
return Database.Query(sql, dbArgs); // Return stuff/unrelated to problem
}
SQL stored in some resource or table. Obviously the parameters must match exactly:
SELECT * FROM companies
WHERE name = :name
-- OR hq = :hq
-- OR ...etc. Doesn't matter since it never gets this far.
Request (Should look clean, but the exact URL format isn't important):
www.website.com/view/companies?hq=fin --> 404: No matching controller
www.website.com/view/companies/hq=fin --> parameters is null
www.website.com/view/companies/hq=fin&name=nokia --> Exception: A potentially dangerous Request.Path value was detected from the client (&).
When I use: [Route("view/{table}{parameters}")] I get:
A path segment cannot contain two consecutive parameters. They must be separated by a '/' or by a literal string. Parameter name: routeTemplate. Makes sense.
My question is: How do I accept a table name and any number of unknown parameters in the usual key1=val1&key2=val2 form (not some awkward indexed format like the one mentioned here) which will be later bound to SQL parameters, preferably using a vanilla data structure rather than something like FormCollection.
I don't think that binding URL parameters to a Dictionary is built-in to the framework. I'm sure there's a way to extend it if you wanted to.
I think quickest (but still acceptable) option is to get the query string parameters using Request.GetQueryNameValuePairs() like this:
[Route("view/{table}")]
public object Get(string table) {
Dictionary<string, string> parameters = Request.GetQueryNameValuePairs()
.ToDictionary(x => x.Key, x => x.Value);
var sql = GetSql(table);
var dbArgs = new DynamicParameters(parameters);
return Database.Query(sql, dbArgs); // Return stuff/unrelated to problem
}

Cannot specify child attributes in the columnset for Retrieve

In attempting to merge contacts in Microsoft CRM, I am using the following code -
//c1ID and c2ID are GUIDs of duplicated contacts.
EntityReference target = new EntityReference();
target.LogicalName = Contact.EntityLogicalName;
target.Id = c2ID;
MergeRequest merge = new MergeRequest();
// SubordinateId is the GUID of the account merging.
merge.SubordinateId = c1ID;
merge.Target = target;
merge.PerformParentingChecks = true;
Contact updater = new Contact();
Contact updater2 = new Contact();
updater = (Contact)xrmSvc.ContactSet.Where(c => c.ContactId.Equals(c1ID)).First();
updater2 = (Contact)xrmSvc.ContactSet.Where(c => c.ContactId.Equals(c2ID)).First();
MergeResponse mergedR = (MergeResponse)xrmSvc.Execute(merge);
When I try my Execute call here,I get this error -
Cannot specify child attributes in the columnset for Retrieve. Attribute: owneridname.
Am I not setting something correctly?
Having updatecontent does not change the issue. In fact, I get the error on lookups entered into the updatecontent. I find you have to build new entityreferences:
if (match.Contains("new_mostrecentcampaign"))
master["new_mostrecentcampaign"] =
new EntityReference(match.GetAttributeValue<EntityReference>("new_mostrecentcampaign").LogicalName
, match.GetAttributeValue<EntityReference>("new_mostrecentcampaign").Id);
...
Merge.UpdateContent = master
...
I realize this is a pretty old question, but for those of you who have run into the same issue in 2021 and beyond, here's the reason this error happens.
TL;DR: Ensure the EntityReference values for the attributes does not specify the Name property.
Explanation:
Everything that gets added to the Entity set to UpdateContent will be applied to the Target contact. When programmatically executing a MergeRequest within a plugin/workflow, the attributes of the UpdateContent get applied (as desired).
Where this breaks down is for EntityReference value types (lookups). The internal Microsoft code that performs this operation tries to interpret all properties of the EntityReference object, including Name.
So when the existing values from the SubordinateId contact are pulled using IOrganizationService.Retrieve (to dynamically get the latest version), the Name property is automatically set for those lookup attributes (the child record). This operation is not valid, even though it's not the user code that's directly executing it.
This brings us full circle to explain the original error:
Cannot specify child attributes in the columnset for Retrieve
I wish I had some documentation for this, but although the official documentation notes that the UpdateContent is optional, experience proves that it is in fact necessary. In the MergeRequests I've tested, I always include that property in the request, and there's a post in the MSDN forums for Dynamics 3.0 that suggests the same.
In fact, when I try to merge two contacts in my org without UpdateContent assigned, I actually get a FaultException saying the following:
Required field 'UpdateContent' is missing
Even though the documentation says it's optional!
So I'd suggest populating the UpdateContent property with something as in the below and see if that works:
var merge = new MergeRequest
{
// SubordinateId is the GUID of the account merging.
SubordinateId = c1ID,
Target = target,
PerformParentingChecks = true,
UpdateContent = new Contact()
};

EntityRef<T> Issues, not returning value from Lookup List

Ok, I'll explain this as much as I can...
I've got a Site Lookup Column called EEE Content Type which refers to the Site Content Item Type Types List.
Now in my custom list (which inherits from Item), I am referencing that column, and it comes up in sharepoint fine and displays the lookup values.
The issue is when I'm using SPMetal.exe to generate the types it whinges about "Key isn't present in the dictionary" and fails. So I remove the definition of the column in the parameters.xml file for SPMetal, and re-generate the classes.
Now I've manually added the property and association.
private EntityRef<SiteContentItemTypeItem> _eeeContentType;
[Association(Name = "EEE_x0020_Content_x0020_Type", Storage = "_eeeContentType", MultivalueType = AssociationType.Single, List = "Site Content Item Types")]
public SiteContentItemTypeItem EEEContentType
{
get
{
return this._eeeContentType.GetEntity();
}
set
{
this._eeeContentType.SetEntity(value);
}
}
SiteContentItemTypeItem inherits from Item so its class is empty.
But when I load the custom list I have created, i get the first entry and the EEEContentType field is null...
using (IntranetDataContext context = new IntranetDataContext("http://siteurl")) {
context.ObjectTrackingEnabled = false;
EntityList<SiteContentItem> alerts = context.GetList<SiteContentItem>("User Alerts");
SiteContentItem alert = (from tmpalert in alerts where tmpalert.Id == 1 select tmpalert).First();
SiteContentItemTypeItem contentType = alert.EEEContentType;
}
I'm all out of ideas...
Should the List value in the Association attribute be that of a collection in the class or is it refering to the actual lookup list name?
Figured it out...
Stupid of me to "assume" when creating site lookup columns via code that SharePoint would use the proper naming conventions for FieldNames with spaces.
So the fieldName was correct, its InternalName wasn't the one I was expecting. And as sharepoint linq requires the internal names, it was throwing internal exceptions in the Linq.SharePoint DLL.

Resources