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

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)

Related

Servicestack Ormlite multi-column constraint fails where constraint includes Enum

I am using ServiceStack.Ormlite, and also make heavy use of the automatic handling of enums whereby they are stored in the db as strings but retrieved and parsed nicely back into Enums on retrieval, so I can do easy type-comparison - say, for a property "UserRole" in the db/table class "User" of enum type "UserRoleEnum" (just for demonstration).
This works great.. until I want to use the enum property to define a multi-column unique constraint
CompositeIndexAttribute(bool unique, params string[] fieldNames);
like:
[CompositeIndex(true, nameof(UserId), nameof(UserRole)]
public class User
{
public long UserId {get;set;}
public UserRoleEnum UserRole {get;set;
}
(as per :
How to Create Unique Constraint with Multiple Columns using ServiceStack.OrmLite? ).
At which time i get:
System.Data.SqlClient.SqlException
Column 'UserRole' in table 'User' is of a type that is invalid for use as a key column in an index.
I currently see options as:
a) Define UserRole as a string (isntead of UserRoleEnum ) in the table entity class and lose the Enum use.... having to manually test the value each time to confirm that the db value is one that i expect in my business logic later
b) Continue to use UserRoleEnum but lose the ability to declare multicolumn uniqueconstraints using the class attribute, and probably have to create these manually using a subsequent db migration script?
Is there any way to make the enums and the multicolumn constraint play nicely, out of the box?
This issue was because enum properties were using the default string definition fallback of VARCHAR(MAX) which SQL Server doesn't let you create indexes on whereas the column definition of a string property is VARCHAR(8000).
This issue is now resolved from this commit which now uses the VARCHAR(255) string definition of the EnumConverter Type Converter. This change is available from v4.5.5 that's now available on MyGet.
Otherwise you can also change the size of the column definition to match a string property by adding a [StringLength] attribute, e.g:
[CompositeIndex(true, nameof(UserId), nameof(UserRole))]
public class User
{
public long UserId { get; set; }
[StringLength(8000)]
public string UserRole { get; set; }
}

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.

Unable to get name from EntityReference

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();

Not tracking a single property of an entity with EF4

My MVC action method receives an entity object (Page) that the default model binder creates from form collection data. Some of the fields are wrong or null because they were not sent in the request to the server, for example I do not send "CreateDate" and the default model binder sets this property to some default value which I don't need.
Once the object is attached it of course tries to persist all the values (including invalid/not needed ones to the database). I could of course assign manually on a per property basis but was wondering if maybe I can somehow flag a property so it is not persisted when EntityState is set to modified and SaveChanges() is called..
public ActionResult SomeMethod(Page page)
{
page.ModifyDate = DateTime.Now;
_db.NewsPages.Attach(page);
_db.ObjectStateManager.ChangeObjectState(page, System.Data.EntityState.Modified);
_db.SaveChanges();
_db.Dispose();
}
The correct way to handle this is using different class for view model, attach empty entity to the context and assign real values per property (or let AutoMapper to handle this scenario) as #Darin suggested in the comment.
If you want to go your way you must not change state of the POCO entity but you must change state of every changed property:
public ActionResult SomeMethod(Page page)
{
page.ModifyDate = DateTime.Now;
_db.NewsPages.Attach(page);
ObjectStateEntry entry = _db.ObjectStateManager.GetObjectStateEntry(page);
entry.SetModifiedProperty("ChangedPropertyName");
// Do the same for all other changed properties
_db.SaveChanges();
_db.Dispose();
}

Proper way to Edit an entity in MVC 3 with the Entity Framework using Data Model First approach?

A majority of the examples I see now are either using the Code First Approach or using an older version of MVC and the Entity Framework.
Assume I have a movie to update and I get to the Edit View, in the Edit method with the Post verb, what is the proper way to update a Movie? The first Edit Method below gets me to the Edit View with the populated Movie values and the second one is the one I want to use to update, I have tried some things, but nothing updates the data.
public ActionResult Edit(int id)
{
var movie = (from m in _db.Movies1
where m.Id == id
select m).First();
return View(movie);
}
[HttpPost]
public ActionResult Edit(Movie movie)
{
try
{
// TODO: Add update logic here
//What do I need to call to update the entity?
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Assuming that _db is derived from ObjectContext you have two options:
Change the state of the entity to Modified:
_db.Movies1.Attach(movie);
_db.ObjectStateManager.ChangeObjectState(movie, EntityState.Modified);
_db.SaveChanges();
This marks all properties of movie as modified and will send an UPDATE statement to the database which includes all column values, no matter if the values really changed or not.
Reload the original entity from the database and apply the changes to it:
var originalMovie = (from m in _db.Movies1
where m.Id == movie.Id
select m).First();
// You actually don't need to assign to a variable.
// Loading the entity into the context is sufficient.
_db.Movies1.ApplyCurrentValues(movie);
_db.SaveChanges();
ApplyCurrentValues will mark only those properties as modified which really did change compared to the original and the UPDATE statement which will be sent to the database only includes the changed column values. So, the UPDATE statement is potentially smaller than in the first example but you have to pay the price to reload the original entity from the database.
Edit
How does the second code example work?
When you run a query using the context (_db) Entity Framework does not only retrieve the entity from the database and assign it to the left side of the query (originalMovie) but it actually stores a second reference internally. You can think of this internal context "cache" as a dictionary of key-value pairs - the key is the entity primary key and the value is the entity itself, the same object as originalMovie refers to.
ApplyCurrentValues(movie) looks up this entity in the context's internal dictionary: It takes the key property value Id of the passed in movie, searches for an entity with that key in the internal dictionary and then copies property by property from the passed in ("detached") movie to the internal ("attached") entity with the same key. EF's change tracking mechanism marks the properties as Modified which were actually different to create later the appropriate UPDATE statement.
Because of this internal reference to the original entity you do not need to hold your own reference: That's the reason why originalEntity is not used in the code. You can in fact remove the assignment to the local variable altogether.
The example would not work if you disable change tracking when you load the original entity - for example by setting _db.Movies1.MergeOption = MergeOption.NoTracking;. The example relies on enabled change tracking (which is the default setting when entities are loaded from the database).
I cannot say which of the two examples has better performance. That might depend on details like size of the entities, number of properties which have been changed, etc.
It's worth to note though that both approaches do not work if related entities are involved (for example movie refers to a category entity) and if the relationship or the related entity itself could have been changed. Setting the state to Modified and using ApplyCurrentValues both affect only scalar and complex properties of movie but not navigation properties.
Your second edit method should look something like this:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var movie = (from m in _db.Movies1
where m.Id == id
select m).First();
if (TryUpdateModel(movie))
{
_db.SaveChanges();
return (RedirectToAction("Index"));
}
return View(movie);
}

Resources