R.Silva
R.Silva

Reputation: 425

EFCore Map 2 entities to same table

I'm trying to use DDD with EFCore and I am struggling to find a way to map 2 POCOs from different context that represent the same entity to the same table.

I have a User class inside UserContext, with all the properties needed to create a new user to my application. And I have either a User class inside my OrderContext, in this class I only have the Id and Email properties, cause it's all that is needed in OrderContext to work.

So I have something like this:

        modelBuilder.Entity<Domain.UserContext.User>(u =>
        {
            u.ToTable("User").HasKey(e => e.Id);
            u.OwnsOne(e => e.Name);
            u.OwnsOne(b => b.HomeAddress);
        });

        modelBuilder.Entity<Domain.OrderContext.User>(u =>
        {
            u.ToTable("User").HasKey(e => e.Id);
        });

        modelBuilder.Entity<Domain.OrderContext.Order>(p =>
        {
            p.ToTable("Order").HasKey(b => b.Id);
            p.HasOne(x => x.User); // this is OrderContext.User
        });

I can't seem to find a way to map both User classes to the same table. Is there a way to do it?

Edit1: Both contexts are bounded context DDD's concept not DbContext. I just need both classes to be maped as the same table. The Add-Migration command return a message telling me that it cannot map 'OrderContext.User' to table 'User' since it is already mapped to 'UserContext.User'.

Upvotes: 28

Views: 31257

Answers (3)

Artiom
Artiom

Reputation: 267

@Smit provided solution should work, but it is not ideal for isolated bounded contexts, where each of them is not aware of one another and each is taking care of it's own configurations.

I have solved this problem by adding separate DbContexts for each bounded context. Each of these contexts inherit base DbContext, where I have my shared logic (like auditing, etc.), and each DbContext inside bounded contexts has it's own DbSets and Fluent Api configurations. This way I have entities which point to the same table, but from different DbContexts.

Upvotes: 7

Artiom
Artiom

Reputation: 267

I am looking at this problem myself. I noticed, that if you specify schema name for one of the tables then EF will not complain. For example in your case:

modelBuilder.Entity<Domain.UserContext.User>(u =>
    {
        u.ToTable("User", "dbo").HasKey(e => e.Id);
        u.OwnsOne(e => e.Name);
        u.OwnsOne(b => b.HomeAddress);
    });

    modelBuilder.Entity<Domain.OrderContext.User>(u =>
    {
        u.ToTable("User").HasKey(e => e.Id);
    });

Of course this is not a full solution and even not a workaround, since you can not have more than 2 mentions of "User" table (that is, in more than 2 contexts).

Also i found https://data.uservoice.com/forums/72025-entity-framework-core-feature-suggestions/suggestions/1872001-map-multiple-entities-to-same-table which makes me think that this in general is not possible.

Regarding DDD in general

Most sources say that your bounded contexts should be isolated not only by code, but also by data. This in theory means, that your User table should be duplicated in each bounded context. This is ideal way, but is unnecessarily complex (imho) for more simple scenarios, since it involves data synchronization across all duplicated tables.

Upvotes: 6

Smit
Smit

Reputation: 2459

The main cause of issue is, EF Core cannot figure out how to use same table for 2 different entities. There is lack of data in mapping, once you fill that in, it works as expected.

First you will need to define how are they related to each other. Sharing same table with same PK does not have Foreign Key defined on server side but there is still intrinsic relationship between both entities which is one-to-one and using PK as FK. Once you define relationship, you will see that it works and both entities are mapped to same table. (Just like how owned entities are mapped to same table). This may not be end of mapping for your case though. Since from EF perspective they are still 2 different entities, except for Id (or PK property), they will have own columns to store data. But what if you have columns which are common in both the context (like Email in your scenario). In such case, you would need to provide mapping for those additional column too. If you map them to same column explicitly, they will start sharing the column in database. Overall the code would look like this.

namespace UserContext
{
    public class User
    {
        public int Id { get; set; }
        public string Email { get; set; }
        // Other properties
    }
}

namespace OrderContext
{
    public class User
    {
        public int Id { get; set; }
        public string Email { get; set; }
    }
}

// In OnModelCreating method
modelBuilder.Entity<UserContext.User>(u =>
{
    u.ToTable("User");
    u.Property(e => e.Email).HasColumnName("Email");
    // Configuration for other properties
});

modelBuilder.Entity<OrderContext.User>(u =>
{
    u.ToTable("User");
    u.Property(e => e.Email).HasColumnName("Email");
    u.HasOne<UserContext.User>().WithOne().HasForeignKey<OrderContext.User>(e => e.Id);
});

Above code creates single table with shared columns and should work as expected. You can add more entities in the same table if you want by following same configuration. Here, I used User from UserContext as principal side but you can use any side. The main reasoning for me was, UserContext.User will be the entity which will be added when adding new User. Entities sharing the table do not have to be subset either. But there will be columns for additional properties which are not shared.

Upvotes: 25

Related Questions