Associations in EF Code First: Part 6 – Many-valued Associations

This is the sixth and last post in a series that explains entity association mappings with EF Code First. I've described these association types so far: Support for many-valued associations is an absolutely basic feature of an ORM solution like Entity Framework. Surprisingly, we’ve managed to get this far without needing to talk much about these types of associations. Even more surprisingly, there is not much to say on the topic—these associations are so easy to use in EF that we don’t need to spend a lot of effort explaining it. To get an overview, we first consider a domain model containing different types of associations and will provide necessary explanations around each of them. Since this is the last post in this series, I'll show you two tricks at the end of this post that you might find them useful in your EF Code First developments.

Many-valued entity associations

A many-valued entity association is by definition a collection of entity references. One-to-many associations are the most important kind of entity association that involves a collection. We go so far as to discourage the use of more exotic association styles when a simple bidirectional many-to-one/one-to-many will do the job. A many-to-many association may always be represented as two many-to-one associations to an intervening class. This model is usually more easily extensible, so we tend not to use many-to-many associations in applications.

Introducing the OnlineAuction Domain Model

The model we introducing here is related to an online auction system. OnlineAuction site auctions many different kinds of items. Auctions proceed according to the “English auction” model: users continue to place bids on an item until the bid period for that item expires, and the highest bidder wins. A high-level overview of the domain model is shown in the following class diagram:
Each item may be auctioned only once, so we have a single auction item entity named Item. Bid is associated directly with Item.

The Object Model

The following shows the POCO classes that form the object model for this domain:
public class User
{
    public int UserId { getset; }
    public string Name { getset; }
 
    public virtual ICollection<Item> BoughtItems { getset; }
}
 
public class Item
{
    public int ItemId { getset; }
    public string Name { getset; }
    public double InitialPrice { getset; }
    public DateTime StartDate { getset; }
    public DateTime EndDate { getset; }
    public int? BuyerId { getset; }
    public int? SuccessfulBidId { getset; }
 
    public virtual User Buyer { getset; }
    public virtual Bid SuccessfulBid { getset; }
    public virtual ICollection<Bid> Bids { getset; }
    public virtual ICollection<Category> Categories { getset; }
}
 
public class Bid
{
    public int BidId { getset; }
    public double Amount { getset; }
    public DateTime CreatedOn { getset; }
    public int ItemId { getset; }
    public int BidderId { getset; }
 
    public virtual Item Item { getset; }
    public virtual User Bidder { getset; }
}
 
public class Category
{
    public int CategoryId { getset; }
    public string Name { getset; }
    public int? ParentCategoryId { getset; }
 
    public virtual Category ParentCategory { getset; }
    public virtual ICollection<Category> ChildCategories { getset; }
    public virtual ICollection<Item> Items { getset; }
}

The Simplest Possible Association

The association from Bid to Item (and vice versa) is an example of the simplest possible kind of entity association. You have two properties in two classes. One is a collection of references, and the other a single reference. This mapping is called a bidirectional one-to-many association. The property ItemId in the Bid class is a foreign key to the primary key of the Item entity, something that we call a Foreign Key Association in EF 4. We defined the type of the ItemId property as an int which can't be null because we can’t have a bid without an item—a constraint will be generated in the SQL DDL to reflect this. We use HasRequired method in fluent API to create this type of association:
class BidConfiguration : EntityTypeConfiguration<Bid>
{
    internal BidConfiguration()
    {
        this.HasRequired(b => b.Item)
            .WithMany(i => i.Bids)
            .HasForeignKey(b => b.ItemId);
    }
}

An Optional One-to-Many Association Between User and Item Entities

Each item in the auction may be bought by a User, or might not be sold at all. Note that the foreign key property BuyerId in the Item class is of type Nullable<int> which can be NULL as the association is in fact to-zero-or-one. We use HasOptional method to create this association between User and Item (using this method, the foreign key must be a Nullable type or Code First throws an exception):
class ItemConfiguration : EntityTypeConfiguration<Item>
{
    internal ItemConfiguration()
    {
        this.HasOptional(i => i.Buyer)
            .WithMany(u => u.BoughtItems)
            .HasForeignKey(i => i.BuyerId);
    }
}

A Parent/Child Relationship

In the object model, the association between User and Item is fairly loose. We’d use this mapping in a real system if both entities had their own lifecycle and were created and removed in unrelated business processes. Certain associations are much stronger than this; some entities are bound together so that their lifecycles aren’t truly independent. For example, it seems reasonable that deletion of an item implies deletion of all bids for the item. A particular bid instance references only one item instance for its entire lifetime. In this case, cascading deletions makes sense. In fact, this is what the composition (the filled out diamond) in the above UML diagram means. If you enable cascading delete, the association between Item and Bid is called a parent/child relationship, and that's exactly what EF Code First does by default on associations created with the HasRequired method.

In a parent/child relationship, the parent entity is responsible for the lifecycle of its associated child entities. This is the same semantic as a composition using EF complex types, but in this case only entities are involved; Bid isn’t a value type. The advantage of using a parent/child relationship is that the child may be loaded individually or referenced directly by another entity. A bid, for example, may be loaded and manipulated without retrieving the owning item. It may be stored without storing the owning item at the same time. Furthermore, you reference the same Bid instance in a second property of Item, the single SuccessfulBid (take another look at the Item class in the object model above). Objects of value type can’t be shared.

Many-to-Many Associations

The association between Category and Item is a many-to-many association, as can be seen in the above class diagram. a many-to-many association mapping hides the intermediate association table from the application, so you don’t end up with an unwanted entity in your domain model. That said, In a real system, you may not have a many-to-many association since my experience is that there is almost always other information that must be attached to each link between associated instances (such as the date and time when an item was added to a category) and that the best way to represent this information is via an intermediate association class (In EF, you can map the association class as an entity and map two one-to-many associations for either side.).

In a many-to-many relationship, the join table (or link table, as some developers call it) has two columns: the foreign keys of the Category and Item tables. The primary key is a composite of both columns. In EF Code First, many-to-many associations mappings can be customized with a fluent API code like this:
class ItemConfiguration : EntityTypeConfiguration<Item>
{
    internal ItemConfiguration()
    {
        this.HasMany(i => i.Categories)
            .WithMany(c => c.Items)
            .Map(mc =>
            {
                mc.MapLeftKey("ItemId");
                mc.MapRightKey("CategoryId");
                mc.ToTable("ItemCategory");
            });
    }
}

SQL Schema

The following shows the SQL schema that Code First creates from our object model:

Get the Code First Generated SQL DDL

A common process, if you’re starting with a new application and new database, is to generate DDL with Code First automatically during development; At the same time (or later, during testing), a professional DBA verifies and optimizes the SQL DDL and creates the final database schema. You can export the DDL into a text file and hand it to your DBA. CreateDatabaseScript on ObjectContext class generates a data definition language (DDL) script that creates schema objects (tables, primary keys, foreign keys) for the metadata in the the store schema definition language (SSDL) file (in the next section, you'll see where this metadata come from):
using (var context = new Context())
{
    string script = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();
}
You can then use one of the classes in the .Net File IO API like StreamWriter to write the script on the disk.
Note how Code First enables cascade deletes for the parent/child relationship between Item and Bid

Get the Runtime EDM

One of the benefits of Code First development is that we don't need to deal with the Edmx file, however, that doesn't mean that the concept of EDM doesn't exist at all. In fact, at runtime, when the context is used for the first time, Code First derives the EDM (CSDL, MSL, and SSDL) from our object model and this EDM is even cached in the app-domain as an instance of DbCompiledModel. Having access to this generated EDM is beneficial in many cases. At the very least, we can add it to our solution and use it as a class diagram for our domain model. More importantly, we can use this EDM for debugging when there is a need to look at the model that Code First creates internally. This EDM also contains the conceptual schema definition language (CSDL) something that drives the EF runtime behavior. The trick is to use the WriteEdmx Method from the EdmxWriter class like the following code:
using (var context = new Context())
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
 
    using (XmlWriter writer = XmlWriter.Create(@"Model.edmx", settings))
    {
        EdmxWriter.WriteEdmx(context, writer);
    }                            
}
After running this code, simply right click on your project and select Add Existing Item... and then browse and add the Model.edmx file to the project. Once you added the file, double click on it and visual studio will perfectly show the edmx file in the designer:
Also note how cascade delete is also enabled in the CSDL for the parent/child association between Item and Bid.

Source Code

Click here to download the source code for the OnlineAuction site that we have seen in this post.

Summary

In this series, we focused on the structural aspect of the object/relational paradigm mismatch and discussed one of the main ORM problems relating to associations. We explored the programming model for persistent classes and the EF Code First fluent API for fine-grained classes and associations. Many of the techniques we’ve shown in this series are key concepts of object/relational mapping and I am hoping that you'll find them useful in your Code First developments.

41 Comments

  • Hi

    Regarding the ItemCategory relationship. Say you wanted to add the possibility to avoid displaying child categories, only in specific relationships.

    In the database you'd have a bit column in the ItemCategory table that could be named DisplayChildCategories.

    How do you include this column in the relationship?
    And where would it be mapped, which class?

  • Great posts. Thank you for sharing.

  • I have been eagerly waiting for Part 6 for a long time. Thanks for the great post.

  • Thank you. Very informative, very helpful post.

  • Hello,


    Thanks for all your posts, they have helped me to understand a lot about code first!


    Could you help me understand what is the best way to map something like this to an existing database/tables?


    Model:


    public class Item
    {
    &nbsp;&nbsp;&nbsp; public int ItemId { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual IDictionary&lt;int, Attribute&gt; Attributes { get; set; }
    }


    public class Attribute
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual IDictionary&lt;int, ExpectedValue&gt; ExpectedValues { get; set; }
    }


    public class ExpectedValue
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    }


    Attribute Table:


    id int
    Name nvarchar(64)
    Description nvarchar(128)


    ExpectedValue Table:


    id int
    Name nvarchar(64)
    Value int
    MonitoredItemAttributeMapID int&nbsp;


    MonitoredItemAttributeMap table (Many to Many Mapping table):


    id int
    MonitoredItemID int
    AttributeID int



    Thank you for taking the time to look at this and any solution is helpful!




    Cheers,


    DJ

  • @Martin H. Normark: Like I described in the post, many-to-many associations cannot have a payload, and if that’s the case then you always have to break it down to two many-to-one association to an intervening class. For example, the following is going to be the object model for the scenario you described:




    public class Item
    {
    &nbsp;&nbsp;&nbsp; public int ItemId { get; set; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;ItemCategory&gt; ItemCategories { get; set; }
    }
    public class Category
    {
    &nbsp;&nbsp;&nbsp; public int CategoryId { get; set; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;ItemCategory&gt; ItemCategories { get; set; }
    }

    public class ItemCategory
    {
    &nbsp;&nbsp;&nbsp; public int ItemCategoryId { get; set; }
    &nbsp;&nbsp;&nbsp; public bool DisplayChildCategories { get; set; }
    &nbsp;&nbsp;&nbsp; public int CategoryId { get; set; }
    &nbsp;&nbsp;&nbsp; public int ItemId { get; set; }

    &nbsp;&nbsp;&nbsp; public virtual Item Item { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual Category Category { get; set; }
    }


    Hope this helps.

  • @DJRodriguez: Like I described in the post a many-to-many association cannot have a payload and in your case, even the Id as a primary key on the MonitoredItemAttributeMap table is considered to be a payload and therefore you can’t create a many-to-many association between the Item and Attribute classes. In this case, like I described in the post, you have to break it down to two many-to-one association to an intervening class. I named this intermediate association class as MonitoredItemAttributeMap and created the following object model for your existing database:



    public class Item
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;MonitoredItemAttributeMap&gt; ItemAttributes { get; set; }
    }

    public class Attribute
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public string Description { get; set; }

    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;MonitoredItemAttributeMap&gt; ItemAttributes { get; set; }
    }

    public class ExpectedValue
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public int Value { get; set; }
    &nbsp;&nbsp;&nbsp; public int MonitoredItemAttributeMapID { get; set; }

    &nbsp;&nbsp;&nbsp; public MonitoredItemAttributeMap ItemAttribute { get; set; }
    }

    public class MonitoredItemAttributeMap
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public int MonitoredItemID { get; set; }
    &nbsp;&nbsp;&nbsp; public int AttributeID { get; set; }

    &nbsp;&nbsp;&nbsp; public Item Item { get; set; }
    &nbsp;&nbsp;&nbsp; public Attribute Attribute { get; set; }
    }

    public class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Item&gt; Items { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Attribute&gt; Attributes { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;ExpectedValue&gt; ExpectedValues { get; set; }

    &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(DbModelBuilder modelBuilder)
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;MonitoredItemAttributeMap&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasRequired(m =&gt; m.Item)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany(i =&gt; i.ItemAttributes)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(m =&gt; m.MonitoredItemID);
    &nbsp;&nbsp;&nbsp; }
    }


    Thanks for your comment and I hope you find this helpful :)

  • Keep up your good work!
    Baba khafan. Baba inkare :)

  • All very good articles. Thank you!

  • merci aghaye manafi babate in post

  • When will other types of collections be supported?
    At least, IEnumerable... sometimes, we don't want a collection to be changed.

  • Awesome post! To me its the best article on EF :)

  • Insert doesn't work if i want to insert a Category with Categories children :(

  • @Alessandro Antonio de Brito: The following object model is all you need:

    public class Address
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Description { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual int PersonId { get; set; }
    }


    public class Person
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;Address&gt; Addresses { get; set; }
    }


    public class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Person&gt; Persons { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Address&gt; Addresses { get; set; }


    &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(DbModelBuilder modelBuilder)
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Person&gt;().Property(p =&gt; p.Id).HasColumnName("ID_PERSON");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Person&gt;().Property(p =&gt; p.Name).HasColumnName("DC_PERSON").IsRequired();
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Person&gt;().ToTable("TB_PERSON");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;


    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Address&gt;().Property(p =&gt; p.Id).HasColumnName("ID_ADDRESS");
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Address&gt;().Property(p =&gt; p.Description).HasColumnName("DC_ADDRESS").IsRequired();
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Address&gt;().Property(p =&gt; p.PersonId).HasColumnName("ID_PERSON");
    &nbsp;&nbsp;&nbsp; }
    }


    As you can see above, you don’t really need to configure the association between Person and Address entities as it will be picked up by convention and Code First will automatically configure it for you. Having a foreign key like PersonId on the Address class is not required but is recommended. Having the foreign key along with the navigation properties will create a foreign key association as opposed to&nbsp;an independent association that have been introduced in the first version of EF.



    Note that it is not necessary to define a PersonA property on the Address class. You should do that only if you want to make your association bidirectional and looks like you don’t have such a requirement in your domain model so I set up a unidirectional association between the two entities. Hope this helps.

  • When did we start changing the associations/configurations from:

    modelBuilder.Entity&lt;Item&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasOptional(i =&gt; i.Buyer)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany(u =&gt; u.BoughtItems)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(i =&gt; i.BuyerId);

    To:

    class ItemConfiguration : EntityTypeConfiguration&lt;Item&gt;
    {
    &nbsp;&nbsp;&nbsp; internal ItemConfiguration()
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.HasOptional(i =&gt; i.Buyer)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany(u =&gt; u.BoughtItems)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(i =&gt; i.BuyerId);
    &nbsp;&nbsp;&nbsp; }
    }

    Are these the same thing?

  • @craigfreeman74: Yes, they are exactly the same thing and have the same effect. Having the fluent API code in a separate class that inherits from EntityTypeConfiguration&lt;T&gt; allows a cleaner way of writing fluent API code and is recommended especially when you have too many entities in your domain that you want to write fluent API code for.

  • Excellent Series!!! Thank you.
    Will you be doing anything in relation to sprocs and views?

    Thanks again.
    Mark

  • Mark Phillips: You’re very welcome.&nbsp;I don’t really have any plans to write about stored procedures or views but if you have any questions, feel free to post them here. I might be able to help :)

  • Hi
    How the data is populated into the the mapping table. eg categoryItem

  • @Farid: The join table (e.g. ItemCategory) is populated when you define new relationships between the related objects (e.g. Item and Category) in your application. For example, the following code will result in a submission of an INSERT INTO command by EF to add a new record to the&nbsp;ItemCategory table:



    Category category = new Category();
    Item item = new Item();

    category.Items.Add(item);
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    context.Categories.Add(category);
    context.SaveChanges();


    And the following code will result in a DELETE command to remove the inserted record:



    category.Items.Remove(item);
    context.SaveChanges();

  • @Jonpa: The code you showed should (and will) work for a many-many association like the one I introduced in this post. Can you please post your object model along with the fluent API codes involving Entity and Entity2? I particularly like to see the Entity2 class definition.

  • The relationship between Entity and Entity2 is an one to many relationship. I think the problem lies in that my foreign key in Entity2 is non nullable (as I want to be).

    Well here's the code (note that in my production code, the entities have better name :) )

    public class Entity : EntityBase
    {
    &nbsp; private ICollection&lt;Entity2&gt; _entities;
    &nbsp; public string Name { get; set; }
    &nbsp; public virtual ICollection&lt;Entity2&gt; Entities
    &nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get { return (_entities ?? (_entities = new HashSet&lt;Entity2&gt;())); }
    &nbsp; }
    }
    public class Entity2 : EntityBase
    {
    &nbsp;&nbsp; public string Name { get; set;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    }
    public abstract class EntityBase
    {
    &nbsp;&nbsp; public Guid Id { get; set; }
    &nbsp;&nbsp; public DateTime Created { get; set; }
    &nbsp;&nbsp; public byte[] Version { get; set; }
    &nbsp;&nbsp; public EntityBase()
    &nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Id = Guid.NewGuid();
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Created = DateTime.Now;
    &nbsp;&nbsp; }
    }
    And my fluent API code:
    public class EntityBaseConfiguration&lt;T&gt; : EntityTypeConfiguration&lt;T&gt; where T : EntityBase
    {
    &nbsp;&nbsp; public EntityBaseConfiguration()
    &nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasKey(e =&gt; e.Id);
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property(e =&gt; e.Version).IsRowVersion();
    &nbsp;&nbsp; }
    }
    class EntityConfiguration : EntityBaseConfiguration&lt;Entity&gt;
    {
    &nbsp;&nbsp; public EntityConfiguration()
    &nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Map(e =&gt; { e.ToTable("Entiteter"); e.MapInheritedProperties(); });
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property(e =&gt; e.Name).IsRequired().HasMaxLength(100);
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMany(e =&gt; e.Entities).WithRequired().WillCascadeOnDelete();
    &nbsp;&nbsp; }
    }
    class EntityConfiguration2 : EntityBaseConfiguration&lt;Entity2&gt;
    {
    &nbsp;&nbsp; public EntityConfiguration2()
    &nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Map(e =&gt; { e.ToTable("Entiteter2"); e.MapInheritedProperties(); });
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Property(e =&gt; e.Name).IsRequired().HasMaxLength(100);
    &nbsp;&nbsp; }
    }

  • @Jonpa: Knew it cannot be a many-to-many association since the exception you were getting was something that usually comes out of a required one-to-many association. Yes, you thought is correct, your code (entity.Entities.Remove(entity2)) would cause Entity’s foreign key in Entity2’s table to become Null but your fluent API code (HasMany(e =&gt; e.Entities).WithRequired()) made that FK column Not Nullable and that’s where the problem happens. To fix this problem I suggest you first add a FK property to your Entity2 class like the following:
    public class Entity2 : EntityBase
    {
    &nbsp;&nbsp;&nbsp; public string Name { get; set;}
    &nbsp;&nbsp;&nbsp; public Guid? EntityId { get; set; }
    }


    And then change your fluent API code in EntityConfiguration where you setup the one-to-many association to this:


    HasMany(e =&gt; e.Entities)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithOptional()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(e =&gt; e.EntityId)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WillCascadeOnDelete();


    And you should be good to go. Hope this helps.

  • @Jonpa: OK, I thought having an Entity2 without an Entity makes sense based on your domain model but if that’s not the case then don’t do it since&nbsp;like you mentioned, your model should exactly reflect your business domain. Based on what you described, the correct way of mapping this association would be as follows:

    public class Entity : EntityBase
    {
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;Entity2&gt; Entities { get; set; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    }


    public class Entity2 : EntityBase
    {
    &nbsp;&nbsp;&nbsp; public string Name { get; set;}
    &nbsp;&nbsp;&nbsp; public Guid EntityId { get; set; }
    &nbsp;&nbsp;&nbsp; public Entity Entity { get; set; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    }


    And the fluent API code to configure the associaion:


    HasMany(e =&gt; e.Entities)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithRequired(e =&gt; e.Entity)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(e =&gt; e.EntityId);


    Note how I turn the association into a bidirectional one so that we can take advantage of it when removing an Entity2 from the Entity. Entities collection:

    // Assuming entity and entity2 are loading from database and that they are related:
    entity.Entities.Remove(entity2);
    entity2.Entity = new Entity();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    context.SaveChanges();



    This way, you can have your required relationship in place without getting any exception when changing the associations between your objects.

  • Ok, but if I want to remove the Entity2 object, or rather delete it. I dont want to assign a new Entity object to the Entity2 object, I only want to delete it. Then I will get the same exception again or am I missing something? Like in the Order -> OrderLine example. How will I design my model to be able to remove an OrderLine from an Order?

    /Jonpa

  • @Jonpa: Then just delete the Entity2 object. For example, in the case of an OrderLine object:


    context.OrderLines.Remove(orderLine);



    Please note that you don’t need to be worried about the existing association between the Order and OrderLine objects, when you delete the OrderLine object, EF automatically updates the OrderLines collection on the related Order. The bottom line is that an OrderLine object can never exists without an Order, you have to either delete it or replace its Order property with another Order be it new or existing.

  • My context only exposes Entity (or Order on the Order, OrderLine example). So the only way to "mingle" with Entity2 (OrderLine) is through Entity. Note that this is exactly what I want. I dont want my context to expose Entity2 because Entity is my aggregate root. So is there a way to achieve this?

  • @Jonpa: An aggregate root is a concept that has to be applied on your Repository classes and not on the definition of the DbContext class. By removing the non-aggregate root DbSets from your DbContext (e.g. DbSet&lt;OrderLine&gt;) you make simple tasks very hard like the way you had to delete the orphan OrderLines by overriding the SaveChanges method. What you are looking for is implemented in some other ORM frameworks though. For example, in NHibernate,&nbsp;associations have a&nbsp;setting&nbsp;called cascade which you can set to delete-orphan. As a result, NHibernate deletes any persistent entity instance that has been removed (dereferenced) from the association (e.g. any persistent OrderLine should be deleted if it’s removed from the OrderLines collection of a persistent Order.). I am also hoping that EF implements these features on the associations but to be honest, I don’t see that happening in the near future.

  • thanks for this great series, very very helpful~ looking forward to your new posts!

  • Never mind my previous comment... The setup worked when I put it in your sample application. Turned out the entity was detached. Setting it to Modified and calling SaveChanges, caused it not save the navigational properties. Perfectly logical ;-)

    Thanks,
    Twan

  • @Jim Shaw: I think you are in the right path. An optional entity association, be it one-to-one or one-to-many, is best represented in an SQL database with a join table. We always try to avoid nullable columns in a relational database schema since information that is unknown degrades the quality of the data you store. That said, EF unfortunately does not have a good support for this type of mapping which means you need to create new entities to represent the join tables for your optional one-to-many relationships. For example, the associations between TextBlock and BlockGroup entities could be represented with the following object model:



    public class TextBlock
    {
    &nbsp;&nbsp;&nbsp; public int TextBlockId { get; set; }
    &nbsp;&nbsp;&nbsp; public string Text { get; set; }
    &nbsp;&nbsp;&nbsp; public string Style { get; set; }
    }
    &nbsp;&nbsp;&nbsp;
    public class BlockGroup
    {
    &nbsp;&nbsp;&nbsp; public int BlockGroupId { get; set; }
    &nbsp;&nbsp;&nbsp; public TextBlock Title { get; set; }
    &nbsp;&nbsp;&nbsp; public ICollection&lt;BlockGroupTextBlock&gt; GroupList { get; set; }
    }


    public class BlockGroupTextBlock
    {
    &nbsp;&nbsp;&nbsp; public int Id { get; set; }
    &nbsp;&nbsp;&nbsp; public int Index { get; set; }
    &nbsp;&nbsp;&nbsp; public int BlockGroupId { get; set; }


    &nbsp;&nbsp;&nbsp; public TextBlock TextBlock { get; set; }
    }
    &nbsp;&nbsp;&nbsp;
    public class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;TextBlock&gt; TextBlocks { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;BlockGroup&gt; BlockGroups { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;BlockGroupTextBlock&gt; BlockGroupTextBlocks { get; set; }


    &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(DbModelBuilder modelBuilder)
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;BlockGroup&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasMany(b =&gt; b.GroupList)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithOptional()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(b =&gt; b.BlockGroupId);


    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;BlockGroup&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasOptional(b =&gt; b.Title)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithRequired();
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;BlockGroupTextBlock&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasRequired(b =&gt; b.TextBlock)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithOptional()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WillCascadeOnDelete();
    &nbsp;&nbsp;&nbsp; }
    }



    Hope this helps.


  • @Ran Davidovitz: Yes, you can define an ItemCategory class and it won’t create a new one for you. The catch is that you will have to change the navigation properties on both sides as well. Something like the following will do the trick:


    public class Item
    {
    &nbsp;&nbsp;&nbsp; public int ItemId { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;ItemCategory&gt; ItemCategories { get; set; }
    }


    public class Category
    {
    &nbsp;&nbsp;&nbsp; public int CategoryId { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;ItemCategory&gt; ItemCategories { get; set; }
    }


    public class ItemCategory
    {
    &nbsp;&nbsp;&nbsp; public int ItemId { get; set; }
    &nbsp;&nbsp;&nbsp; public int CategoryId { get; set; }
    }
    &nbsp;&nbsp;&nbsp;
    public class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Item&gt; Items { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Category&gt; Categories { get; set; }


    &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(DbModelBuilder modelBuilder)
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;ItemCategory&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasKey(ic =&gt; new { ic.CategoryId, ic.ItemId });
    &nbsp;&nbsp;&nbsp; }
    }

    That being said, I think you should NOT do this. Not sure how it helps you with data entry, but for that you can write a custom initializer class and then override its Seed method where you can then use the ExecuteSqlCommand to execute raw SQL statements for your data entry. Hope this helps.

  • Thanks for your articles, i learned a lot from them. I'm doing my first steps in EF, and still dont know many things about it. Can you give me a hint how to solve my problem?

    I have classes:

    [Table("Firmy")]
    public class Firma
    {
    &nbsp;&nbsp; [Key]
    &nbsp;&nbsp; [DatabaseGenerated(DatabaseGeneratedOption.None)]
    &nbsp;&nbsp; public int FirmaId{ get; set; }
    &nbsp;&nbsp;
    &nbsp;&nbsp; [MaxLength(150), Required]
    &nbsp;&nbsp; public string Nazwa { get; set; }
    &nbsp;&nbsp; [MaxLength(50), Required]
    &nbsp;&nbsp; [DisplayName("Nazwa skrócona")]
    &nbsp;&nbsp; public string NazwaKrotka { get; set; }
    &nbsp;&nbsp; public int? Telefon { get; set; }
    &nbsp;&nbsp; public int? Fax { get; set; }
    &nbsp;&nbsp; [MaxLength(150)]
    &nbsp;&nbsp; public string Ulica { get; set; }
    &nbsp;&nbsp; //[MaxLength(5)]
    &nbsp;&nbsp; public int Kod { get; set; }
    &nbsp;&nbsp; [MaxLength(150)]
    &nbsp;&nbsp; public string Miasto { get; set; }
    &nbsp;&nbsp; [MaxLength(150), Required]
    &nbsp;&nbsp; [DisplayName("E-mail")]
    &nbsp;&nbsp; public string Email { get; set; }
    &nbsp;&nbsp; public bool Aktywny { get; set; }
    &nbsp;&nbsp; public virtual ICollection&lt;FirmaOddzialHistoria&gt; Oddzialy { get; set; }
    }
    [Table("FirmyOdddzialyHistoria")]
    public class FirmaOddzialHistoria
    {
    &nbsp;&nbsp; [Key]
    &nbsp;&nbsp; [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    &nbsp;&nbsp; public int FirmaOddzialHistoriaID { get; set; }
    &nbsp;&nbsp; public virtual Firma Firma { get; set; }
    &nbsp;&nbsp; public virtual FirmaOddzial FirmaOddzial { get; set; }
    &nbsp;&nbsp; public DateTime DataStart { get; set; }
    &nbsp;&nbsp; public DateTime DataKoniec { get; set; }
    }
    [Table("FirmyOddzialy")]
    public class FirmaOddzial
    {
    &nbsp;&nbsp; [Key]
    &nbsp;&nbsp; [DatabaseGenerated(DatabaseGeneratedOption.None)]
    &nbsp;&nbsp; public int OddzialId{ get; set; }
    &nbsp;&nbsp; public int? Telefon { get; set; }
    &nbsp;&nbsp; public int? Fax { get; set; }
    &nbsp;&nbsp; [MaxLength(150)]
    &nbsp;&nbsp; public string Ulica { get; set; }
    &nbsp;&nbsp; [MaxLength(5)]
    &nbsp;&nbsp; public string Kod { get; set; }
    &nbsp;&nbsp; [MaxLength(150)]
    &nbsp;&nbsp; public string Miasto { get; set; }
    &nbsp;&nbsp; [MaxLength(150), Required]
    &nbsp;&nbsp; public string Email { get; set; }
    &nbsp;&nbsp; public bool Aktywny { get; set; }
    &nbsp;&nbsp; public virtual ICollection&lt;FirmaOddzialHistoria&gt; Firmy { get; set; }
    &nbsp;&nbsp; public virtual ICollection&lt;UzytkownikHistoria&gt; Uzytkownicy { get; set; }
    &nbsp;&nbsp; public Limit Limit { get; set; }
    }

    and i want do something like this (this code don't work):

    repository.FirmyOddzialy.Where(p =&gt; p.Firmy.DataStart&lt;=DateTime.Now &amp;&amp; p.Firmy.DataStop&gt;=DateTime.Now &amp;&amp; p.Firmy.Firma.FirmaId==1)

    Best regards

  • @JohneBee: Something like this will do the trick:



    var query = (from o in repository.FirmyOddzialy
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from f in o.Firmy
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where f.DataStart &lt;= DateTime.Now &amp;&amp; f.DataStop &gt;= DateTime.Now &amp;&amp; f.Firma.FirmaId == 1
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; select o);

  • Thanks for your articles, i learned a lot from them.

    How can i make an Unidirrectional 1 to many relation.&lt;b&gt; and make it &nbsp;cascade on delete &lt;/b&gt;

    Here is my models :

    &nbsp;public class User
    {
    &nbsp;&nbsp;&nbsp; public int UserId { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;Shipment&gt; Shipments { get; set; }
    }
    public class Shipment
    {
    &nbsp;&nbsp;&nbsp; public int ShipmentId { get; set; }
    }

    Any User May have 0~many shipments.I want to casccade the corresponded shipments and delete them automaticly when i delete &nbsp;an user.

    I dont want make the relation BiDirrectional.and eneable DeleteOnCascade from other side by make the Virtual User Property [Requierd].

    thanks.

  • @Rasul: Your code pretty much creates your desired object model, except that you need to add a UserId to the Shipment class as the foreign key property:

    public class User
    {
    &nbsp;&nbsp;&nbsp; public int UserId { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;Shipment&gt; Shipments { get; set; }
    }

    public class Shipment
    {
    &nbsp;&nbsp;&nbsp; public int ShipmentId { get; set; }
    &nbsp;&nbsp;&nbsp; public int UserId { get; set; }
    }

    class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;User&gt; Users { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Shipment&gt; Shipments { get; set; }
    }

  • @fengyj: The exception that you are getting is very normal and pretty much explains what is going on. Think about it, you're asking EF to add two objects for you in one transaction: an Item object who has a Bid object as its SuccessfulBid which happens to have the very same Item object as its Item. EF first tries to insert the Bid object in order to get a SuccessfulBidId for the Item object to insert afterwards but then it notices that the Bid object also needs the very same Item object as its Item which means the Item has to be inserted first to give its id to the Bid. Which one is going to go first? It’s obvious that this circular dependency makes it impossible to have them both inserted in one single transaction.
    Therefore, the only way to make it work is to use two transactions to add the objects like the following code:



    using (UnitOfWork db = new UnitOfWork())
    {
    &nbsp;&nbsp;&nbsp; Item item = db.Items.Add(new Item() { ItemId = 1 });
    &nbsp;&nbsp;&nbsp; db.SaveChanges();



    &nbsp;&nbsp;&nbsp; item.SuccessfulBid = new Bid()
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BidId = 1,
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Item = item
    &nbsp;&nbsp;&nbsp; };
    &nbsp;&nbsp;&nbsp; db.SaveChanges();
    }

  • Great posts! I've just started to work with EF code first model and after 5 blog parts all pieces come into place.

  • Thank you.
    This is great post.

  • Why are the navigation IDs exposed in the object model (e.g. BuyerId, SuccessfulBidId, etc.). Shouldn't these be encapsulated? These appear to be implimentation details (that could change) and should be hidden from the outside world. A user should never need to know the BuyerId, only the Buyer.

  • I'm still not clear on how all those navigation IDs get set correctly. Do the owning objects have to have explicit code to set or maintain all those navigation ids or does EF handle them somehow? If it does, how does it determine which object owns another (it could appear in the collections, or details, of more than one owner object)?

    Consider a User that sells an Item to another User. How is the BuyerId FK set on the Item?

Comments have been disabled for this content.