How to fix "Properties whose types are collection of primitives or complex types are not supported" with EF DbContext? - entity-framework

I have a project containing POCO entities. A database context has been created for it using Entity Framework 4.2 and code first. This works fine, but the context needs to be exposed as an OData service which does not work.
Browsing to the OData service gives this error:
The property 'DataSubmissionItems' on type
'Lifecycle.ProgramReportSubmission.Model.ProgramReportSubmission' is
not a valid property. Properties whose types are collection of
primitives or complex types are not supported.
The data service class looks like:
public class ExceptionReportDataService : DataService<ExceptionReportEntitiesContext>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.UseVerboseErrors = true;
}
}
The EF context class looks like:
public class ExceptionReportEntitiesContext : DbContext
{
public DbSet<ExceptionReport> ExceptionReports { get; set; }
public ExceptionReportEntitiesContext()
: base(DynamicConfig.GetAppSettingValue("DB_CONN_STRING_LIFECYCLE"))
{
}
}
The POCO entities look like:
namespace WBRT.ProgramData.Lifecycle.ExceptionReportModel
{
public class ExceptionReport
{
public virtual Guid ExceptionReportID { get; set; }
public virtual Lifecycle.ProgramReportSubmission.Model.ProgramReportSubmission ReportSubmission { get; set; }
}
}
namespace Lifecycle.ProgramReportSubmission.Model
{
public class ProgramReportSubmission
{
public Guid ProgramReportSubmissionId { get; set; }
public virtual ICollection<DataSubmissionItem> DataSubmissionItems { get; set; }
}
public class DataSubmissionItem
{
public Guid DataSubmissionItemId { get; set; }
}
}
What I've tried:
Setting DataServiceKey on the DataSubmissionItem class
Setting ProxyCreationEnabled to false on the ExceptionReportEntitiesContext constructor as well as in the data service
Overriding OnModelCreating and removing the IncludeMetadataConvention
Overriding OnModelCreating and setting modelBuilder.Entity<ProgramReportSubmission.Model.ProgramReportSubmission>().Ignore(prs => prs.DataSubmissionItems);
Note: I can't introduce a dependency on the EntityFramework DLL in the POCO entities project as this affects referencing projects that still run .NET 3.5.
Anyone know how to resolve this error?

THe RTM version of WCF DS doesn't support these kind of properties. But the latest CTP does. http://blogs.msdn.com/b/astoriateam/archive/2011/10/13/announcing-wcf-data-services-oct-2011-ctp-for-net-4-and-silverlight-4.aspx.
On the other hand, the fact that you get such an error probably means that WCF DS doesn't recognize the provider as EF, and istead works with it as with a reflection provider. So even the latest CTP won't really fix that problem.
WCF DS currently only recognizes EF provider if the T in DataService is ObjectContext or derived type. The typical workaround for EF Code First is to define the service as DataService and then override the CreateDataSource method on it and return the ObjectContext implementation from your DbContext. See this article about how to do that (it's about EF 4.1, but I think the same will apply to 4.2 as well): http://social.technet.microsoft.com/wiki/contents/articles/5234.aspx. You can skip down to the part about WCF DS.

Related

EntityFramework how to not map a class but do map it's inherited properties

We use EntityFramework 6.1 with CodeFirst in our web mvc application (StdWebApp). Now we want to make a new custom version of this application (CustomWebApp) .
The CustomWebApp will use most of the code of the standard one, in it's domain model it will extend the Person class.
In CustomDomain we make implement a new DbContext that must connect with the database of the custom app (CustomSqlDb).
In (C#) code there is no problem that there is a Person in Domain and in CustomDomain. However we have not been able to devise a mapping for Person in the Custom DbContext that will:
Create a single "Person" table.
Contains fields form "CustomDomain.Person" AND those from "Domain.Person".
We tried some variants like this:
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
}
);
using this document as our inspiration msdn mapping types
But EF complains about the simple name beeing equal.
Obviously we could rename the "Person" in "CustomDomain" to "PersonCustom" but that could lead to a lot of silly names if we have to do this again in the future like "PersonCustomExtraSpecial" etc.
Thoughts anyone?
UPDATE
we tried the solution suggested by mr100, here is the complete code:
namespace Domain
{
public class Person
{
public int Id { get; set; }
public string Stuff { get; set; }
}
}
namespace CustomDomain
{
public class Person : Domain.Person
{
public string ExtraStuff { get; set; }
}
}
namespace CustomDomain
{
public class DbModel : DbContext
{
DbSet<CustomDomain.Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomDomain.Person>().Map(m => m.ToTable("Person"));
}
}
}
This still result in the error
The type 'CustomDomain.Person' and the type 'Domain.Person' both have the same simple name of 'Person' and so cannot be used in the same model. All types in a given model must have unique simple names. Use 'NotMappedAttribute' or call Ignore in the Code First fluent API to explicitly exclude a property or type from the model.
So we added the following code:
namespace CustomDomain
{
public class DbModel : DbContext
{
DbSet<CustomDomain.Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Domain.Person>();
modelBuilder.Entity<CustomDomain.Person>().Map(m => m.ToTable("Person"));
}
}
}
Still same result.
To achieve this your DbContext class in CustomWebApps should have property People defined like this:
public DbSet<CustomDomain.Person> People {get; set;}
and no property:
public DbSet<Domain.Person> People {get; set;}
even if it comes from StdWebApp DbContext class from which CustomWebApp DbContext class may derive (if that is the case for you). Additionally you may set properly table name:
modelBuilder.Entity<Person>().ToTable("Person");

How do you get a web API odatamodelbuilder to work with EF fluent API mappings

I have created a fairly simply domain model using pocos. I have mapped these to an EF DB context using EntityTypeConfiguration<TEnitityType> classes. This all works fine.
I am now trying to create an OData V4 WebAPI controller endpoint using a ODataConventionModelBuilder and this is where things are coming unstuck. It all works fine until it encounters an association that is not convention based. But I cannot find a way to get the ODataBuilder to pick up the mappings from my EntityTypeConfiguration<TEnitityType> classes.
This leaves my with 2 unpalatable options
Decorate my lovely clean pocos with dirty attributes.
Re-map all the non convention based mappings manually using the ODataBuilder
Not sure if code samples will help but here they are anyway, i have simplified the entities for brevity.
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Item>("Items");
config.MapODataServiceRoute(
routeName: "odata",
routePrefix: "odata",
model: builder.GetEdmModel(),
batchHandler: new DefaultODataBatchHandler((GlobalConfiguration.DefaultServer)));
public class Item
{
public Int32 Id { get; set; }
public Int16 ItemTypeId { get; set; }
public virtual ItemType Type { get; set; }
public virtual ICollection<ItemVersion> Versions { get; set; }
public virtual ICollection<ItemTag> Tags { get; set; }
}
The problem comes when it encounters the ItemTags collection, here is an ItemTag:
public class ItemTag
{
public Int32 ItemId { get; set; }
public string Tag { get; set; }
public Item Item { get; set; }
}
Which you can see is not convention based and I have a configuration class for it as follows:
public class ItemTagConfiguration : EntityTypeConfiguration<ItemTag>
{
public ItemTagConfiguration()
{
HasKey(x => new {x.ItemId, x.Tag});
HasRequired(x => x.Item)
.WithMany(y => y.Tags)
.HasForeignKey(x => x.ItemId);
}
}
Does anyone know of a way that I can use these EntityTypeConfiguration files with an ODataBuilder or web API?
EDIT
If found this page which seems to indicate it might be possible with EF 6 which I am using. What I want to do is this
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Dbf>("Dbfs");
// modelBuilder.Configurations.Add(new DbfMap()); <---- NO GOOD - Needs Class from DBContext we only have a model builder :(
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
but the builder does not have a Configurations property.
Two things:
I have read multiple sources now that ward against using lazy loading and serialization; which is basically what OData is; (It even uses the system.runtime.serialization.datacontract and datamember attributes)
I have had more success in explicitly loading from context, and defining navigation properties in the modelbuilder for dbContext. I understand you are looking at customized nav properties, but I am fairly sure these are overriden methods useful for the ODataModelBuilder class (that does not assume much and needs less Entity Framework to work). Where you mentioned using EF already, I imagine that is the direction you will work, and if you do not need to alias your model names, you add an entry for each Set, using convention naming.
EntitySet("routePrefixName")
in building the EdmModel, and it wires up the relationships you made using Fluent previously. If you do have to add extraneous items to the underlying model, you should define each class as an EntityType<>(), only setting the key. EdmBuilder can use mild properties and key association to attach to the EF model in the ODataConventionModelBuilder.
I have wrestled and sought for some time, and there does not seem to be a wealth of information on .Net OData v4 floating around, probably due to the whole force datetimeoffset issue.
Hope that helps somewhat

ASP.NET MVC4 Web API Controller serialization

I am trying to create a RESTful web service that returns a list of products using ASP.NET MVC4 Web API. Here is my controller class
public class ProductController : ApiController
{
public IEnumerable<Product> GetProducts()
{
WebCatalogContext dbcontext = DatabaseConfig.Instance.Context;
List<Product> plist = dbcontext.Products.ToList();
return plist;
}
}
When I run my service and call the following URL from my browser :/api/Product, I get System.Runtime.Serialization.SerializationException. I looked into my plist object and there is no problem with it.
Here is my data model:
[DataContract(Name = "p")]
[Serializable]
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DataMember(Name = "id")]
public int Id { get; set; }
[Required, MaxLength(50)]
[DataMember(Name = "ti")]
public string Title { get; set; }
[Required]
[DataMember(Name = "de")]
public string Description { get; set; }
[Required]
[DataMember(Name = "ph")]
public string PhotoURL { get; set; }
[DataMember(Name = "ca")]
public virtual ProductCategory Category { get; set; }
}
[DataContract(Name="pc")]
[Serializable]
public class ProductCategory
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DataMember(Name="id")]
public int Id { get; set; }
[DataMember(Name="nm")]
public string Name { get; set; }
}
When I remove the reference to ProductCategory from my Product class, all things work just fine. But, when I include it I get the following exception.
Type 'System.Data.Entity.DynamicProxies.Product_664E9A0AA1F165A26C342B508BFFF1279FD3FE059285225BDA19F407A29A9CAD' with data contract name 'Product_664E9A0AA1F165A26C342B508BFFF1279FD3FE059285225BDA19F407A29A9CAD:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
Any idea about what I am missing?
Regards
Entity Framework has wrapped your POCO with an EF Proxy POCO so it can perform lazy loading - this uses the Virtual attribute to create a 'lazy-loadable' navigation property. I expect that is where the serialization error comes from.
You could make a new class and map the POCO to that - then pass the DTO style class from the controller. I've never returned an EF object directly from the API (I always map to some something else) so I don't know another option.
EF POCO to DTO (data transfer object) is relatively painless if you use a tool like http://valueinjecter.codeplex.com/ or http://automapper.org/
To support Lazy Loading for navigation properties which is declared as virtual, EF will generate the proxies for any models which have navigation properties which leads to this kind of exception.
For very simple application, you can use model from EF as DTOs (if having no navigation properties), but for complex application, you should do separate and differ between DTOs and domain models. It should not be mapping 1:1 between DTO and domain model.
Therefore, in your case, you create more DTO model for Web API layer, it will be fine.

API Versioning with ASP.NET Web API and Entity Framework

I am developing a REST API using ASP.NET Web API, Code-First Entity Framework 5 and SQL Server 2012 and I need to be able to version the API. I've read a few blog posts and articles about indicating the API version either in the URI or in a custom HTTP header and using a custom IHttpControllerSelector to select different ApiControllers based on the indicated version. This all makes sense.
What I'm struggling to figure out is how to manage the affects of versioning beyond the Web API layer, specifically in Entity Framework. How do I go about evolving my DbContext without breaking older versions of the API? Can I version the DbContext as well? And if so, how?
What I ended up doing was combining the Repository Pattern with Pablo's answer. The gist of it is that my EF models are versioned, I use EF Code-First Migrations to migrate the database to the new versions of the models, my DbContext always works with the latest version of the models, I developed a number of concrete repositories that each implement the IRepository<TItem> interface below.
public interface IRepository<TItem> : IQueryable<TItem>, ICollection<TItem>, IDisposable
where TItem : class
{
void Update(TItem item);
void SaveChanges();
}
One implementation of IRepository<TItem> is DbRepository<TItem> which wraps the entity framework code used to talk to the database.
public class DbRepository<TItem> : IRepository<TItem>
where TItem : class
{
private MyDbContext _db;
public DbRepository()
{
_db = new MyDbContext();
}
// Implementation of IRepository<TItem> methods
}
Another implementation of IRepository<TItem> is TypeConversionRepository<TExternal,TInternal> which is an abstract class that facilitates converting from one model type to another.
public abstract class TypeConversionRepository<TExternal, TInternal> : IRepository<TExternal>
where TExternal : class
where TInternal : class
{
protected IRepository<TInternal> InternalRepository { get; set; }
protected abstract TInternal ConvertInbound(TExternal externalItem);
protected abstract TExternal ConvertOutbound(TInternal internalItem);
// Implementation of IRepository<TItem> methods
}
Methods that return models or accept models as parameters use ConvertInbound() and ConvertOutbound() to convert models of type TExternal to TInternal and vice versa. Therefore, given the following 2 versions of MyModel, we can write 2 versions of MyModelRepository; version 2 can talk directly to the database while version 1 will need to convert from version 2 back to version 1.
namespace Models.v1
{
public class MyModel
{
public int Id { get; set; }
public string MyProperty { get; set; }
}
public class MyModelRepository : TypeConversionRepository<Models.v1.MyModel,Models.v2.MyModel>
{
MyModelRepository()
{
this.InternalRepository = new Models.v2.MyModelRepository();
}
protected override TInternal ConvertInbound(TExternal externalItem)
{
return new Models.v2.MyModel
{
Id = externalItem.Id,
MyNewProperty = externalItem.MyProperty
};
}
protected override TExternal ConvertOutbound(TInternal internalItem)
{
return new Models.v1.MyModel
{
Id = internalItem.Id,
MyProperty = internalItem.MyNewProperty
};
}
}
}
namespace Models.v2
{
public class MyModel
{
public int Id { get; set; }
public string MyNewProperty { get; set; }
}
public class MyModelRepository : DbRepository<MyModel>
{
}
}
Now the v1 ApiController can use the v1 MyModelRepository, the v2 ApiController can use the v2 MyModelRepository, but in the end all requests utilize a database that has been migrated to v2.
I think it is a good practice to evolve the Web API and the underline DB model separately (or EF model). That means a DTO model for the Web API, which is mapped to the EF Model in the Web API. That layer of indirection will give you the chance to make changes that perhaps only affects the Web API or the EF model. In addition, a new version in Web API might not impact directly in existing EF model. For example, a new version of the Web API that uses a completely different set of tables.
Regards,
Pablo.

Using Automapper, mapping DTOs back to Entity Framework including referenced entities

I've got POCO domain entities that are persisted using Entity Framework 5. They are obtained from the DbContext using a repository pattern and are exposed to a RESTful MVC WebApi application through a UoW pattern. The POCO entities are proxies and are lazy loaded.
I am converting my entities to DTOs before sending them to the client. I am using Automapper to do this and it seems to be working fine with Automapper mapping the proxy POCOs to DTOs, keeping the navigation properties intact. I am using the following mapping for this:
Mapper.CreateMap<Client, ClientDto>();
Example of Domain/DTO objects:
[Serializable]
public class Client : IEntity
{
public int Id { get; set; }
[Required, MaxLength(100)]
public virtual string Name { get; set; }
public virtual ICollection<ClientLocation> ClientLocations { get; set; }
public virtual ICollection<ComplianceRequirement> DefaultComplianceRequirements { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
public class ClientDto : DtoBase
{
public int Id { get; set; }
[Required, MaxLength(100)]
public string Name { get; set; }
public ICollection<ClientLocation> ClientLocations { get; set; }
public ICollection<ComplianceRequirementDto> DefaultComplianceRequirements { get; set; }
public ICollection<Note> Notes { get; set; }
}
Now I am trying to update my context using DTOs sent back up from the wire. I am having specific trouble with getting the navigational properties/related entities working properly. The mapping for this I'm using is:
Mapper.CreateMap<ClientDto, Client>()
.ConstructUsing((Func<ClientDto, Client>)(c => clientUow.Get(c.Id)));
Above, clientUow.Get() refers to DbContext.Set.Find() so that I am getting the tracked proxy POCO object from EF (that contains all of the related entities also as proxies).
In my controller method I am doing the following:
var client = Mapper.Map<ClientDto, Client>(clientDto);
uow.Update(client);
client successfully is mapped, as a proxy POCO object, however it's related entities/navigational properties are replaced with a new (non-proxy) POCO entity with property values copied from the DTO.
Above, uow.Update() basically refers to a function that performs the persist logic which I have as:
_context.Entry<T>(entity).State = System.Data.EntityState.Modified;
_context.SaveChanges();
The above doesn't persist even persist the entity, let alone related ones. I've tried variations on the mappings and different ways to persist using detaching/states but always get "an object with the same key already exists in the ObjectStateManager" exceptions.
I've had a look at countless other threads and just can't get it all working with Automapper. I can grab a proxy object from the context and manually go through properties updating them from the DTO fine, however I am using Automapper to map domain -> DTO and it would be alot more elegant to use it to do the reverse, since my DTOs resemble my domain objects to a large extent.
Is there a textbook way to handle Automapper with EF, with Domain Objects/DTOs that have navigational properties that also need to be updated at the same time?
UPDATE:
var originalEntity = _entities.Find(entity.Id);
_context.Entry<T>(originalEntity).State = System.Data.EntityState.Detached;
_context.Entry<T>(entity).State = System.Data.EntityState.Modified;
The above persistence logic updates the 'root' EF proxy object in the context, however any related entities are not updated. I'm guessing that this is due to them not being mapped to EF proxy objects but rather plain domain objects. Help would be most appreciated!
UPDATE:
It seems that what I'm trying to achieve is not actually possible using the current version of EF(5) and that this is a core limitation of EF and not to do with Automapper:
Link
Link
I guess it's back to doing it manually. Hope this helps someone else who is wondering the same.
You have allready identified the problem:
The above persistence logic updates the 'root' EF proxy object in the
context, however any related entities are not updated
You are setting the modified state on the root node only. You must write code to iterate through all the objects and set the state to modified.
I implemented a pattern to handle this hierarchy model state with EF.
Every entity model class implements an interface like below, as do the view model classes:
public interface IObjectWithState
{
ObjectState ObjectState { get; set; }
}
The ObjectState enumeration is defined below:
public enum ObjectState
{
Unchanged = 0,
Added = 1,
Modified = 2,
Deleted = 3
}
For example when saving a deep hierarchy of objects using EF, I map the view model objects to their equivalent entity objects, including the ObjectState.
I then attach the root entity object to the context (and consequently all child objects):
dbContext.MyCustomEntities.Attach(rootEntityObj);
I then have an extension method on the DbContext that loops through all the items in the context's change tracker and update each entity's state (as you have done above).
public static int ApplyStateChanges(this DbContext context)
{
int count = 0;
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.ObjectState);
if (stateInfo.ObjectState != ObjectState.Unchanged)
count++;
}
return count;
}
Then we can simply save the changes as normal:
dbContext.SaveChanges();
This way, all the hierarchy of child objects will be updated accordingly in the database.
What you want to do is get the Entity from the database first:
var centity = _context.Client.First(a=>a.Id = id)
Then you map over this and update (this is what you were looking for, it will only map things it finds in the inputDTO, and leave the other properties alone)
Mapper.Map<UpdateClientInput, Client>(inputDto, centity);
_context.update();