Unable to get name from EntityReference - dynamics-crm

I'm using CRM 2015 SDK for my plugin. I want the attributes of entity reference in my code. I'm able to get the Guid and Logical Name. But the name returns null for all the entity reference fields. Here is my code:
EntityReference centre= ((EntityReference)quoteEntity.Attributes["mc_centre"]);
Guid centreGuid = centre.Id; //returns Guid
string centreName = centre.Name; //returns null
I have checked the Referenced Entity, "Centre" which uses the 'name' field and has valid value. Has anyone faced the same issue? Am I doing something wrong in my code? I don't want another service call to get the name btw.

the Name property of an EntityReference is not always populated when you cast it from an attribute.
You need to do an additional retrieve if you want to get the name.
This is the relevant MSDN article: EntityReference.Name Property
This property can contain a value or null. This property is not
automatically populated unless the EntityReference object has been
retrieved from the server.

Entity member = service.Retrieve("new_vendor", ((EntityReference)entity["new_vendorname"]).Id, new ColumnSet(true));
or
Entity member = service.Retrieve("new_vendor", Vendor.Id, new ColumnSet(true));
String VendorName = member.Attributes["new_name"].ToString();

Related

How to pass an EntityReference to add attribute value on a lookup field in Microsoft Dynamics 365 CRM

Entity contact = new Entity("contact");
contact.Attributes.Add("fullname", "h api test");
contact.Attributes.Add("emailaddress1", "hh#devenv1.local");
contact.Attributes.Add("telephone1", "1");
contact.Attributes["parentcusotmerid"] = new EntityReference("Organization", );
Guid contactId = m_OrgServ.Create(contact);
Console.WriteLine(contactId);
The lookup field I want to set
The logicalname of the lookup field is parentcusotmerid, and
m_OrgSerc.create
is basically
Service.create
I am setting attribute values for the fields, it works fine for normal text boxes where I am entering values, however for lookup values it doesn't work. I know lookup fields are of type EntityReference, so I need to know the LogicalName of the entity the lookup is pointing and the Id of the record.
I have tried it but its asking for the GUID of the Organization field now, so I'm not sure if I am going about it the right way?
You cannot set "parentcustomerid" to organization. It's special reference field that takes either Account or Contact entity reference as parameter.
If you want to set it you go like this
contact.Attributes["parentcusotmerid"] = new EntityReference("account", Guid.NewGuid());
or
contact.Attributes["parentcusotmerid"] = new EntityReference("contact", Guid.NewGuid());
where Guid.NewGuid() is Guid of your Account or Contact that you want to reference

Retrieve Customer Entity in Dynamics CRM 2016

As we know Dynamics CRM has a specific attribute value: Customer. This value combines the Client and Account entity, but I'm blind or MSDN doesn't have specification about retrieving this field in query.
For example:
QueryByAttribute query = new QueryByAttribute(entName);
query.ColumnSet = new ColumnSet(new String[] { searchAttr });
query.Attributes.Add(searchAttr);
query.Values.Add(searchValue);
EntityCollection retrived = service.RetrieveMultiple(query);
This code accepts entity name and searches the attribute's name and value, but when I run it I don't know which type of entity I get from my DataSouce: Client or Account.
So the question is: is it possible to retrieve Customer entity in one query?
No, you must first know which entity you are trying to retrieve.
Get the value held within the Customer field as an EntityReference:
var customer = entity.GetAttributeValue<EntityReference>("customerid");
Get the LogicalName of the EntityReference:
var customerEntity = customer.LogicalName;

Why model validation is not working

I have following Model class that is used during the Web API Post. As you can see Id field is annotated as Required.
public class Model
{
[Required]
public Guid Id { get; set; }
}
The Post for API is as follows
[HttpPost]
public IActionResult Post([FromBody]Model value)
{
if (!ModelState.IsValid)
return BadRequest();
Model newModel = new Model() { Id = value.Id };
return Ok(newModel);
}
On a sunny day, this is what I see. All good
enter image description here
On a rainy day, when Id is not provided, I get following.
enter image description here
Given that in the second example, a Required field is not provided, shouldn't a BadRequest is returned rather than a 200 with invalid id guid with 00000000-0000-0000-0000-000000000000?
Using the Required annotation
In the Web API Docs, your issue is known as 'under-posting'.
The issue is that Guid has a default value, so when no value is provided, it is initialized with the default value... which then satisfies the Required constraint.
To prevent this, counter-intuitively you make the Guid nullable using Guid?.
Then if the value is not provided, the deserializer will set the value to null, which will cause the Required constraint to be violated.
If the value is provided, it will be set, and all will be well.
See https://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api for more details, there is an example of a similar issue for a decimal property.
The key distinction to understand is that Guid.Empty is really a valid Guid. The only way to distinguish between the user providing a Guid (which is empty) and not providing one at all is to make it nullable, so null = not provided, and Empty = the user provided the empty Guid.
Using a Custom Annotation
If you really don't want to make your Guid nullable, you need to consider what would you do for a 'normal' value type, e.g. an integer. Rather than making it required, you'd use a Range attribute and specify that it must be > 0.
Similarly for Guids, you'd ideally have an attribute that would simply test that it is != Guid.Empty
See https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation#custom-validation for guidance on creating a custom validation atribute.

Why is my Enitty.Contains(attributeField) returning false when I am able to set the value?

I have a block of code that is not working as I thought it would.
I have set an Entity up as follows and have a previous guid.
parentEnt = new Entity("vehicle_ent");
parentEnt.id = guid;
Now when I do a check with a statement:
if (parentEnt.Contains("attribute_field")) {
parentEnt["attribute_field"] = "test";
}
The above will never be called because the if statement fails.
However, if I remove the if statement. I am able to actually assign and run the code:
parentEnt["attribute_field"] = "test";
Is there something I am missing with the Contains Method? I thought it was used to check if the Entity contains the attribute?
On the Entity class, you can always assign an attribute like the example you provided whether or not it exists. If it exists, it will overwrite it (which is what you discovered).
So
parentEnt["attribute_field"] = "test";
Will always work, whether or not the attribute already has a value assigned.
When you run the constructor for a CRM entity object, and assign it a guid
Like
Entity parentEnt = new Entity("vehicle_ent");
parentEnt.id = guid;
you are creating a new object of the entity type with the 'vehicle_ent' logical name and a id of 'guid' At this point all the attribute/properties that belong to an entity with that name, are not created along with the entity object, and you only have an Entity class object with a LogicalName and id set.
If you want to check if an entity record with that id contains a certain attribute, you need to fetch is from the database, using your the organization service, like
ColumnSet attributes = new ColumnSet(true);
parentEnt = _service.Retrieve("vehicle_ent", guid, attributes);
After the retrieve is called you can check if the entity record contains the attribute you need to check.
I just add a couple of things:
The syntax entity[attributename] and entity.Attributes[attributename] are equivalent, the reason can be found inside the Entity metadata:
public object this[string attributeName] { get; set; }
the method maps at entity level the Attributes property (the type of this property is AttributeCollection an inherit from DataCollection<string,object> and the base type is an IEnumerable<KeyValuePair<TKey, TValue>>)
DataCollection contains this method:
// Summary:
// Gets or sets the value associated with the specified key.
//
// Parameters:
// key:
// Type: TKey. The key of the value to get or set.
//
// Returns:
// Type: TValue The value associated with the specified key.
public virtual TValue this[TKey key] { get; set; }
this method adds the key (our attributename) inside the collection if the key is not present before. For this you can assign a value to an attribute without using the Contains method first. Of course when you read the value you need to check if the key is present, this is the purpose of the Contains method, but to read the values the GetAttributeValue can be used as well (but it's necessary to pay attention to the default values returned when the attribute is not inside the collection)

Setting data type in ODataConventionModelBuilder

I've seen in OData documentation that there are Edm types Date and Time. Currently in my database there are many DATE fields being represented in EF as DateTimes, so the ODataConventionModelBuilder is declaring them as Edm.DateTime. How can I change them to Edm.Date?
Was hoping I could do this:
entityType.Property(p => p.AgreementDate).EdmType = EdmType.Date;
The corresponding Edm type of certain property is mapped from the CLR type and can't be overrided by ODataConventionModelBuilder.
If you want to change the Edm type, you can ignore these properties within the ODataConventionModelBuilder.
After getting the Edm model by calling GetEdmModel on the ODataConventionModelBuilder, you can add these properties with Edm.Date to the Edm model by calling OData APIs.
Here's my answer in case someone is interested in the details of how to implement Feng Zhao's suggestion. I didn't find the API too discoverable, so I wanted to share.
First, build your EDM model as usual with the ODataConventionModelBuilder but ignore the date property:
...
entitType.Ignore(p => p.AgreementDate);
...
IEdmModel model = builder.GetEdmModel();
Then add the date property "manually":
var myType = (EdmStructuredType)model.FindDeclaredType(typeof(MyType).FullName);
var dateType = (IEdmPrimitiveType)model.FindType("Edm.Date");
myType.AddProperty(new EdmStructuralProperty(myType, "AgreementDate", new EdmPrimitiveTypeReference(dateType, false)));
That's it.

Resources