I am using EF and AutoMapper with Azure table storage emulation.
I have two types, Fragment (serializable DTO) and FragmentEntity (EF persisted DO). I use AutoMapper to map from the DTO to the DO for persistence on a PUT call and vice versa on a GET call.
The problem is my inexperience with both EF and AutoMapper. I have configured the FragmentEntity class (according to the documentation I can find) to be generating the Id for the Fragment at the database (see below).
Entity ID definition:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
At runtime the http client makes a REST PUT call with the new content (Fragment). This is received via WCF and serialized into a Fragment as follows.
[OperationContract]
[WebInvoke(UriTemplate = "/Fragments", Method = "PUT")]
void PutFagment(Fragment fragment);
During debugging if I step over the code the received ID value is null (as I have defined the ID property as optional).
public void PutFagment(Fragment fragment)
{
var fragmentEntity = Mapper.Map<Fragment, FragmentEntity>(fragment);
_fragmentContext.Add(fragmentEntity);
}
[DataMember]
public virtual int? Id { get; set; }
I am not sure if defining it as optional was a wise move; I did this because if it is not optional the value defaults to zero which automapper then maps to the target FragmentEntity and subsequently all keys end up being zero (clearly wrong) in the database.
As you may expect I have also made the FragmentEntity Id optional to avoid setting a value in the application code, my hope was if it was null the database generation may occur however this doesn’t appear to be happening.
<Fragment><Id i:nil="true"/><Name>Drei</Name><Type>3</Type></Fragment>
My problems:
- I think the decision to make Id optional in both classes is incorrect, I assume there is some AutoMapper configuration which will appropriately handle this case and that both can remain non-optional.
- I am not sure why the DatabaseGenerated attribute is not taking effect.
So, it turns out that the developer is responsible with azure table storage for generating their unique rowkey. This is done for the FragmentEntity which extends TableServiceEntity. Aka the provider will not support database key generation, the developer must own this.
This means:
TableServiceEntitys, instead, just provide a sensible mapping in the AutoMapper configuration in both directions.The ID should not be specified by the client (it is a server responsibility). So the mapping from DTO to persisted DO overwrites whatever value is set by default with a newly generated Guid (see code below).
Then the mapping in the other direction from the FragmentEntity to the Fragment must be configured to set the Id attribute of the DTO to the RowKey value of the FragmentEntity.
My mapping code is registered in my Global.asax.cs to handle the mapping registrations. I will move this into an IoC container at some point.
As a result of this change I no longer define an ID in my FragmentEntity and the defined Id attribute in the Fragment class is no longer optional.
NB: I used Guid because it is easier to generate unique Guid values than to track int keys and increment. Also this means I’m not introducing a db-lock bottleneck while checking which int key value is next.