In our last post Separating your ODATA Models from the Persistence Layer with AutoMapper I showed how we can separate our public API Model from the internal persistence layer. In this post I will show how we can re-model the public API model without changing the internal data model.
In the example from the previous post I showed our model Customer that contained a few properties regarding the creation and modification of the entity:
public class Customer
{
[Key]
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public string ContractId { get; set; }
[Required]
public long CreatedById { get; set; }
[ForeignKey(nameof(ModifiedById))]
public User ModifiedBy { get; set; }
[Required]
public long ModifiedById { get; set; }
[ForeignKey(nameof(ModifiedById))]
public User ModifiedBy { get; set; }
[Required]
public DateTimeOffset Created { get; set; }
[Required]
public DateTimeOffset Modified { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
However it seems that most of the information is not really necessary or useful for the caller. Therefore I would like to hide these properties in a normal response and only reveal them upon sepcific request via an ODATA $expand option.
This is possible with the help of AutoMapper. We just have to refactor our model to this:
public class EntityDetails
{
[Required]
public long CreatedById { get; set; }
[Required]
public long ModifiedById { get; set; }
[Required]
public DateTimeOffset Created { get; set; }
[Required]
public DateTimeOffset Modified { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class Customer
{
[Key]
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public string ContractId { get; set; }
public EntityDetails Details { get; set; }
}
Now our model contains a subclass with the required information. In order to map the information from the underlying data access model we have to extend our mapping profile (from the previous post) to this:
public class EntityAutoMapperProfile : Profile
{
public EntityAutoMapperProfile()
{
CreateMap<CustomerDataAccess, Customer>()
.ForMember(dest => dest.Details, opt => opt.MapFrom(src => new EntityDetails
{
CreatedById = src.CreatedById,
ModifiedById = src.ModifiedById,
Created = src.Created,
Modified = src.Modified,
RowVersion = src.RowVersion,
}))
.ReverseMap();
}
}
With this mapping we transform the separate properties from the data access entity to the EntityDetails class (we just have to make sure that we use a Lambda Expression).
The only thing that is missing is to tell ODATA that EntityDetails is a known entity, otherwise we will get an error similar to this:
message=No NavigationLink factory was found for the navigation property 'Details' from entity type 'Net.Appclusive.Public.Domain.EntityDetails' on entity set 'Customers'. Try calling HasNavigationPropertyLink on the EntitySetConfiguration. Parameter name: navigationProperty
For more information on this error have a look at ODataController throws ‘No NavigationLink factory was found’ when returning a different type in IHttpActionResult.
void Initialise(System.Web.Http.OData.Builder.ODataConventionModelBuilder builder)
{
builder.ComplexType<EntityDetails>();
}
Note: if you use builder.EntitySet("EntityDetails") the retrieval/return of these objects works as well, but we cannot use these objects to post/send them from the client to the server.
After adding the line above to our model builder we can query our entity set via Invoke-RestMethod 'http://appclusive/api/Core/Customers(1)?$expand=Details' and get the following output:
{
"odata.metadata": "http://appclusive/api/Core/$metadata#Customers/@Element",
"Id": "1",
"Name": "Edgar Schnittenfittich",
"Description": "Our first customer"
"ContractId": "arbitrary-contract-id",
"Details":
{
"CreatedById": "1",
"ModifiedById": "1",
"Created": "2016-08-15T05:42:08.01Z",
"Modified": "2017-01-01T08:05:42.01Z",
"RowVersion": "AAAAAAABvVY="
}
}
And when querying the entity without any options it just returns:
{
"odata.metadata": "http://appclusive/api/Core/$metadata#Customers/@Element",
"Id": "1",
"Name": "Edgar Schnittenfittich",
"Description": "Our first customer"
"ContractId": "arbitrary-contract-id"
}
With this approach we can now freely modify the layout of our public models without changing the internal database models and schema.

How would you do this with a self-referencing property?
I am not quite sure if I understand exactly what you mean. We have models where we use self referencing (to build a tree). I can post youi the code for this if you are interested.
I am not quite sure if I understand exactly what you mean. We have models where we use self referencing (to build a tree). I can post youi the code for this if you are interested.