Polymorphic cross-associations on Entity Framework - c#

OK, this is an interesting and most importably real urgent problem for me to solve... In order for others to neatly comprehend it, I've stretched myself to make a well illustrated post.
The Object Model
So I have this simple, easy and "beautiful" model in mind. See the first picture. (You can ignore PathEntry, it's not relevant in my situation.)
The idea is that a MediaFeedItem owns:
a collection of ThumbnailFileEntries (accesible through the ThumbnailFiles property)
at most 1 raw FileEntry (MetadataFile property) and
at most 1 MediaFileEntry (MediaFile property)
We shall refer to these last three entity types as the file entities.
Now there's more: As you can see, I am inheriting both ThumbnailFileEntry and MediaFileEntry from FileEntry, and let's not debate that! (for now), it's one of those end-of-story aspects of the design and both entity types will continue to grow later on.
This already brings me some significant issues right away in regards to the polymorphic associations induced by the relationships from the file entities to MediaFeedItem.
The first thing that you shall observe is that I have eliminated the navigation property from the derived file entities (ThumbnailFileEntry and MediaFileEntry) to the primary entity MediaFeedItem.
I do this because they already inherit that property defined in the base class FileEntry. As you can see, I do not delete the roles at the end of these associations.
The Relational Model
I shall be using the so-vastly-conceptually-superior TPT strategy for generating and mapping my Object Model to the RDB world (vs TPH/TPC).
I'm using EF5-rc, the EDMX model designer to design my model, and the EF5 DbContext Generator to generate a DbContext and POCOs cuz I wanna use the DbContext API.
As you can see, I can nicely generate the database model using the EF tools:
The Problem
When loading a new MediaFeedItem and saving it, I get the following error:
System.InvalidOperationException: Multicplicity constraint violated. The role 'MetadataFile' of the relationship 'MediaFeedModel.MediaFeedItem_MetadataFile' has multiplicity 1 or 0..1.
What am I doing wrong?

Looking at your problem one thing stands out, The FK relationship between File and MediaFeedItem is required (IE a file must have a MediaFeedItem), but in the case where you are in an extended version of File you probably dont want this.
What i think you want to do is one of the following:
change the multiplicity on MediaFeedItem_FileEntry to 0..1 - 0..1 so that it isnt required at either end
create a new extended type to handle your metadataFile type and remove the direct reference between the base type and MediaFeedItem
I personally think the second is a more elegant solution to your problem as its creating an actual type for your MetadataFile
What appears to be happening is that you are trying to create an extended type but the base type isnt actually a metadata file.

Related

Orchard model inheritance with Table-Per-Concrete-Type?

I know that the underlying ORM used in Orchard is NHibernate and it does support the so-called ClassMapping which may help customize the mappings the way we want.
However I'm not sure about how Orchard utilizes the mapping methods supported by NHibernate. In this case it seems to always use the strategy similar to Table Per Type in EF as well as some other ORMs. With that strategy, the base class will be mapped to some common table whereas the derived class will be mapped to another table which contains all properties of its own (not declared in the base class). Those 2 tables will have a one-one relationship.
Now I really want to make it use the strategy similar to Table Per Concrete Type in which the base and derived classes will be mapped to 2 different tables with all properties (including inherited properties) being mapped to columns. Those 2 tables will not have any relationship, so querying for columns in just one table will not unexpectedly generate an inner JOIN (for one-one relationship).
Actually that requirement makes sense in case we just need to partition our data (from 1 big table to 2 or more small tables that have the same schema). We don't want to re-declare or use some kind of duplicate model classes (with different names), instead we just need to create a new model class and let it inherit from one base model class containing all necessary properties.
With the current code like this:
public class ARecord {
//properties ...
}
public class BRecord : ARecord {
//empty here
}
Currently we cannot use BRecord because it is understood as another part of the ARecord, the auto-generated query (always with INNER JOIN) will be failed because of some does-not-exist table or column names.
How can I solve this?
You're not going to like it ;) In a nutshell, the answer is don't do inheritance at all. Orchard was very deliberately designed around the idea of composition, steering well clear of inheritance in its content models. Maybe the central idea in Orchard is to make the concept of content part the "atom of content", and to design those basic units as very simple and composable pieces of functionality that do one thing well.
After all these years, the concept has held remarkably well, and I've yet to see an example of a content model where inheritance would have been more elegant and appropriate. This is reflected in the way nHibernate is customized and used in Orchard, as you've discovered.
So the solution to your problem can likely be one of two things:
You're modeling contents, and you should re-think your approach towards composition of parts. If you give a little more details about your specific scenario (maybe in a new question), I'm happy to help specifically in this direction.
You're modeling non-content data, in which case you might want to consider opting out of Orchard's specific nHibernate content-specialized idiosyncrasies and do things closer to the metal. Again, if you give more specifics about your scenario, I'm happy to take a look and give some pointers.

AutoMapper.Collection.EntityFramework causing InvalidOperationException after Persist.InsertOrUpdate

I am using AutoMapper and trying out AutoMapper.Collection.EntityFramework, specifically the Persist<T> method.
My "source" is a fairly large object graph that has been converted (by AutoMapper) into some EntityFramework entities. The parent entity is called Log.
In my experimental test, I do the following:
var mapper = collectionConfig.CreateMapper();
var persistence = dbContext.Logs.Persist(mapper);
var testLog = logs.First(); // "logs" is the output of an AutoMapper.Map of a collection.
persistence.InsertOrUpdate<Log>(testLog);
Assert.IsTrue(dbContext.ChangeTracker.HasChanges());
What happens is an exception at the ChangeTracker.HasChanges call:
System.InvalidOperationException: The property 'Id' is part of the
object's key information and cannot be modified.
The stack trace is:
System.Data.Entity.Core.Objects.EntityEntry.DetectChangesInProperty(Int32
ordinal, Boolean detectOnlyComplexProperties, Boolean detectOnly)
at
System.Data.Entity.Core.Objects.EntityEntry.DetectChangesInProperties(Boolean
detectOnlyComplexProperties) at
System.Data.Entity.Core.Objects.ObjectStateManager.DetectChangesInScalarAndComplexProperties(IList`1
entries) at
System.Data.Entity.Core.Objects.ObjectStateManager.DetectChanges()
at System.Data.Entity.Core.Objects.ObjectContext.DetectChanges() at
System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean
force) at
System.Data.Entity.Infrastructure.DbChangeTracker.HasChanges()
This is a fairly well-known and well-documented exception: it happens when you have an existing EntityFramework entity object and attempt to change the value of one of its primary key property fields.
But I'm not doing that anywhere.
I never set an Id value anywhere in my code. (The value for the Id property comes from elsewhere and is set by AutoMapper when the list of Log objects is created. Because of the nature of the data I'm reasonably sure that an entry for the testLog object does already exist in the database.)
I've been able to save whole collections of Log entries, created by AutoMapper, to the database, through EF, so I don't think my Log entities or the object graph have the problem. I think it's something AutoMapper.Collection.EntityFramework is somehow doing.
I did try the Persist method with a different, simpler entity, with many fewer child entities, and didn't have this problem. But I can't even tell from this error which object in the graph has the supposedly-changed Id value: half the objects in this object graph have a primary key called Id.
I can confirm that the actual value of testLog.Id isn't changed by the InsertOrUpdate. But my attempts to examine an Entry<Log> for testLog or even to look at dbContext.Logs.Local all cause the same exception to be thrown.
So: anyone have an idea why this is happening?
Thanks to Tyler Carlson of AutoMapper.Collection, I have an answer.
persistence.InsertOrUpdate<Log>(testLog);
The problem stemmed from testLog already being of type Log, which is the same type I'm using in EntityFramework. As Tyler said:
Automapper doesn't support mapping to itself, as it causes so many
problems.
[...]
What you should be doing is passing
the dto into the persist call, not the entity it mapped to. That
mapping will happen in the InsertOrUpdate call.
We also discovered that if your Entity and Dto objects share a common base class, and that base class contains the definition of the key fields, this will also happen.
So in my situation, where my Entity and Dto contained 95% of the same fields, derived from a common base class, I had to take the Id property that was the primary key out of the base class, and define it separately in the two classes. That ensured that, while the properties had the same names, they weren't in any sense the same property from a Reflection point of view.
If you're curious about the details, the discussion is here: AutoMapper.Collection Issue 40.

EF DB-first mapping mess

I have a frustrating situation owing to this little quirk of EF. Here's a simple demo of the behavior. First the DB schema:
As you see, RestrictedProduct is a special case of product, which I'm intending to make a subclass of Product with some special code.
Now I import to an EF data model:
Oops! EF saw that RestrictedProduct had only 2 fields, both FKs, so it mapped it as a one-to-many relationship between Product and Restriction. So I go back to the database and add a Dummy field to RestrictedProduct, and now my EF model looks much better:
But that Dummy field is silly and pointless. Maybe I could delete it? I blow away the field from the DB table and the entity model, then refresh the model from the DB...
Oh, no! The Product-Restriction association is back, under a new name (RestrictedProduct1)! Plus, it won't compile:
Error 3034: Problem in mapping fragments starting at lines (x, y) :Two entities with possibly different keys are mapped to the same row. Ensure these two mapping fragments map both ends of the AssociationSet to the corresponding columns.
Is there any way to prevent this behavior, short of keeping the Dummy field on the RestrictedProduct table?
I just came across the same issue, and as an alternative to putting the dummy field in your RestrictedProduct table to force the creation of an entity you can also make your RestrictedProduct.RestrictionId field nullable and EF will then generate an entity for it. You can then modify it to use inheritance and any subsequent "Update model from database" will not cause undesired nav properties. Not really a nice solution but a work around.
Let's walk slowly into your problem.
1st thing you need to decide is if the restricted product is
really a special case of product or is it a possible extension
to each product.
From your original DB Scheme it seems that any product may have
a relation to a single restriction however a single restriction
can be shared among many products.. so this is a simple 1 to many
situation which means that restricted product is NOT a special case
of product! Restriction is an independent entity which has nothing
to do with product in a specific way.
Therefore EF is correct in the 1st importation of your scheme:
1. a product can have 0 or 1 restrictions.
2. a restriction is another entity which can be related to many products.
I do not see your problem.

Modelling polymorphic associations database-first vs code-first

We have a database in which one table contains records that can be child to several other tables. It has a "soft" foreign key consisting of the owner's Id and a table name. This (anti) pattern is know as "polymorphic associations". We know it's not the best database design ever and we will change it in due time, but not in the near future. Let me show a simplified example:
Both Event, Person, and Product have records in Comment. As you see, there are no hard FK constraints.
In Entity Framework it is possible to support this model by sublassing Comment into EventComment etc. and let Event have an EventComments collection, etc.:
The subclasses and the associations are added manually after generating the basic model from the database. OwnerCode is the discriminator in this TPH model. Please note that Event, Person, and Product are completely different entities. It does not make sense to have a common base class for them.
This is database-first. Our real-life model works like this, no problem.
OK. Now we want to move to code-first. So I started out reverse-engineering the database into a code first model (EF Power Tools) and went on creating the subclasses and mapping the associations and inheritance. Tried to connect to the model in Linqpad. That's when the trouble started.
When trying to execute a query with this model it throws an InvalidOperationExeception
The foreign key component 'OwnerId' is not a declared property on type 'EventComment'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
This happens when I have bidirectional associations and OwnerId is mapped as a property in Comment. The mapping in my EventMap class (EntityTypeConfiguration<Event>) looks like this:
this.HasMany(x => x.Comments).WithRequired(c => c.Event)
.HasForeignKey(c => c.OwnerId);
So I tried to map the association without OwnerId in the model:
this.HasMany(x => x.Comments).WithRequired().Map(m => m.MapKey("OwnerId"));
This throws a MetaDataException
Schema specified is not valid. Errors:
(10,6) : error 0019: Each property name in a type must be unique. Property name 'OwnerId' was already defined.
(11,6) : error 0019: Each property name in a type must be unique. Property name 'OwnerId' was already defined.
If I remove two of the three entity-comment associations it is OK, but of course that's not a cure.
Some further details:
It is possible to create a working DbContext model ("code second") from the edmx by adding a DbContext generator item. (this would be a work-around for the time being).
When I export the working code-first model (with one association) to edmx (EdmxWriter) the association appears to be in the storage model, whereas in the original edmx they are part of the conceptual model.
So, how can I create this model code-first? I think the key is how to instruct code-first to map the associations in the conceptual model, not the storage model.
I personally stick with Database first when using EF on any schema that is this level of complexity. I have had issues with complex schemas in regards to code first. Maybe the newer versions are a little better, but worrying how to try and code complex relationships seems less straight forward then allowing the engine to generate it for you. Also when a relationship gets this complex I tend to avoid trying to generate it with EF and try and use stored procedures for easier troubleshooting of performance bottlenecks that can arise.

Entity Framework 4 and SQL Server 2008 Multiple Possible Foreign Keys

I am trying to come up with a database design that would work with Entity Framework 4 Code First. Actually, I have no experience yet of EF4 Code First but as I understand it, if I write the code, it will create the database and tables.
The issue is this. There are various types of auctions, they all have some common fields and some specific ones. In the code I envisage having a base abstract class called Auction and subclasses like LowestUniqueBidAuction and EnglishForwardAuction etc.
Nothing surprising there. The problem is that I imagine the database structure to mimic this. I imagine an Auction table and a LowestUniqueBidAuction table and a EnglishForwardAuction table. In the Auction table I imagine a foreign key into one of these two tables for each row depending on the type of auction that that row is. I also imagine another column in the Auction table with the name of the derived auction table (such as EnglishForwardAuction).
The problem is that whenever I've ever created a foreign key I've had to specify the name of the foreign table into which the key points (which makes sense). In this case, however, there is one of many tables that the key could point. So there are many issues here. Firstly, I could simply not use a foreign key and just use an ordinary field, but then the database will not be able to maintain data consistency for me. The second issue is how will EF Code First handle this? In other words, how will it know that if I ask for all EnglishForwardAuction rows from the Auction table that it should look at the column with the table name and then join on the EnglishForwardAuction table to get the extra fields?
Has anyone ever faced similar issues?
Thanks,
Sachin
This problem is solvable in Entity Framework in a number of ways - read up on how EF handles inheritance and what strategies are available.
There are basically three strategies how to handle this:
(1) Table per Hierarchy
You have only one single table, that represents all possible sub classes. Of course, this means, several rows (that only exist in a given subclass) must be nullable, since they don't show up / don't exist in super classes or other subclasses.
(2) Table per Type
Each subclass gets its own table, and by default, the sub-types table shares the PK with the base classes' table - e.g. PK = 1 in Auction will also be PK = 1 in EnglishForwardAuction. So your subclass tables reference the base table - not the other way around.
(3) Table per Concrete Type
Each concrete subclass (your separate auction types) gets its own table, but that table contains everything - all the columns, from that specific type, but also its base type.
Read more here:
Inheritance in the Entity Framework
Inheritance and Associations with Entity Framework Part 1
Entity Framework Modeling: Table Per Hierarchy Inheritance
Entity Framework Modeling: Table Per Type Inheritance
Searching for Entity Framework Inheritance and/or one of these strategies will reveal a lot more hits, too - that topic is very well covered and discussed on the interwebs! :-)

Categories

Resources