Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF6: "entity types cannot share table" error without any table splitting

I have created an EntityFramework 6 data context (code first) to represent some tables in a database: as for the repro code quoted below, one (Items) contains the items of some kind of dictionary, another (Notes) contains some notes which can be attached to items. An item can contain 0 or more notes. Nothing difficult here: I'll just have 1 POCO object for each table, and this is what I'm doing; yet, when initializing the database from my DbContext I get the error:

"The entity types 'EfItem' and 'Item' cannot share table 'Item' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them."

Now, this error seems to be meaningful for cases where several objects are mapped to the same SQL table, but this is not the case here. I have 1 and only 1 concrete object for each of the tables. So, what could be the reason of this error?

I have created a repro solution you can download from https://1drv.ms/u/s!AMHCfliT740PkK9H, which needs further explanation.

The main point in this solution is that I need to keep the client code totally agnostic of the storage technology of choice, which might be a RDBMS, a NoSql DB, etc. To this end, in my solution I have a "core" project, a PCL, which contains the interface for a high-level repository dealing only with interfaces. For instance, I have methods like AddItem(IItem item), rather than directly using a concrete type.

This allows me to customize the concrete objects as required by the technology of choice: for instance, EF objects require "parent" navigation properties, while these would not make sense in MongoDB. Of course, the downside of this approach is having to redefine most of the concrete types, and converting them into their interface-implementing counterparts: for instance, in EF I have an EfItem object, which can be converted into IItem. Note that this object does not implement this interface, but is simply convertible to it: EF forces me to use concrete types in navigation properties (see e.g. How to use interface properties with CodeFirst), so that e.g. the item's Notes collection is of type INote in the core project, but of type EfNote in the EF project.

As for the EF6 project, I have added a couple of POCO objects to represent items and notes, and a DbContext derived class with a DbSet for each type (EfItem, EfNote). Note that these objects are plain, concrete types with no direct relationship to either any other concrete type, and often they are not even implementing the corresponding core interface. For instance, in the core project I have an Item concrete type implementing IItem, but it's completely independent from EfItem (EfItem has List<EfNote> Notes while IItem has List<INote> Notes). There is no inheritance, and no table splitting; EfItem is mapped to SQL table Item, and EfNote to Note.

Here is the essential code for my EF objects:

public class EfItem
{
    public string Id { get; set; }
    public string SortId { get; set; }
    public string ParentId { get; set; }
    public string Head { get; set; }
    public short ElementCode { get; set; }
    public int LegacyNumber { get; set; }
    public int Flags { get; set; }
    public string Lemma { get; set; }
    public short Homograph { get; set; }
    public string Content { get; set; }
    public DateTime TimeCreated { get; set; }
    public string CreatorId { get; set; }
    public IList<EfNote> Notes { get; set; }
}
public class EfNote
{
    public string Id { get; set; }
    public string ItemId { get; set; }
    public Item Item { get; set; }
    public string BookmarkId { get; set; }
    public string UserId { get; set; }
    public string Text { get; set; }
    public byte Level { get; set; }
    public DateTime TimeCreated { get; set; }
}

Here is my EF context:

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Conventions.Remove<PluralizingTableNameConvention>();
    // item
    mb.Entity<EfItem>().ToTable("Item");
    mb.Entity<EfItem>().Property(i => i.Id).IsFixedLength().HasMaxLength(32)
        .IsRequired().IsUnicode(false);
    mb.Entity<EfItem>().Property(i => i.SortId).HasMaxLength(500)
        .IsRequired().IsUnicode(false);
    mb.Entity<EfItem>().Property(i => i.ParentId).IsFixedLength().HasMaxLength(32)
        .IsUnicode(false);
    mb.Entity<EfItem>().Property(i => i.Head).HasMaxLength(50).IsUnicode(true);
    mb.Entity<EfItem>().Property(i => i.ElementCode).IsRequired();
    mb.Entity<EfItem>().Property(i => i.LegacyNumber).IsRequired();
    mb.Entity<EfItem>().Property(i => i.Flags).IsRequired();
    mb.Entity<EfItem>().Property(i => i.Lemma).HasMaxLength(200).IsRequired();
    mb.Entity<EfItem>().Property(i => i.Homograph).IsRequired();
    mb.Entity<EfItem>().Property(i => i.Content).IsRequired().IsUnicode(true)
        .HasColumnType("xml");
    mb.Entity<EfItem>().Property(i => i.TimeCreated)
        .IsRequired()
        .HasColumnType("datetime2");
    mb.Entity<EfItem>().Property(i => i.CreatorId).HasMaxLength(100).IsRequired();
    // notes
    mb.Entity<EfNote>().ToTable("Note");
    mb.Entity<EfNote>().Property(n => n.Id).IsFixedLength().HasMaxLength(32)
        .IsRequired().IsUnicode(false);
    mb.Entity<EfNote>().Property(n => n.ItemId).IsFixedLength().HasMaxLength(32)
        .IsRequired().IsUnicode(false);
    mb.Entity<EfNote>().Property(n => n.BookmarkId)
        .HasMaxLength(50).IsUnicode(false).IsRequired();
    mb.Entity<EfNote>().Property(n => n.UserId).HasMaxLength(100).IsRequired();
    mb.Entity<EfNote>().Property(n => n.Text).HasMaxLength(2000).IsRequired();
    mb.Entity<EfNote>().Property(n => n.Level).IsRequired();
    mb.Entity<EfNote>().Property(n => n.TimeCreated).IsRequired()
        .HasColumnType("datetime2");
    base.OnModelCreating(mb);
}

The SQL tables definitions are:

CREATE TABLE [dbo].[Item](
    [Id] [char](32) NOT NULL,
    [SortId] [varchar](500) NOT NULL,
    [ParentId] [char](32) NULL,
    [Head] [nvarchar](50) NULL,
    [ElementCode] [smallint] NOT NULL,
    [LegacyNumber] [int] NOT NULL,
    [Flags] [int] NOT NULL,
    [Lemma] [nvarchar](200) NOT NULL,
    [Homograph] [smallint] NOT NULL,
    [Content] [xml] NOT NULL,
    [CreatorId] [nvarchar](100) NOT NULL,
    [TimeCreated] [datetime2](7) NOT NULL,
 CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)

CREATE TABLE [dbo].[Note](
    [Id] [char](32) NOT NULL,
    [ItemId] [char](32) NOT NULL,
    [BookmarkId] [varchar](50) NOT NULL,
    [UserId] [nvarchar](100) NOT NULL,
    [Text] [nvarchar](2000) NOT NULL,
    [Level] [tinyint] NOT NULL,
    [TimeCreated] [datetime2](7) NOT NULL,
 CONSTRAINT [PK_Note] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)
like image 809
Naftis Avatar asked Nov 23 '25 05:11

Naftis


1 Answers

In your WorkContext.cs

Change

public DbSet<EfItem> Items { get; set; }

to

public DbSet<EfItem> EFItems { get; set; }

Also in method

protected override void OnModelCreating(DbModelBuilder mb)

b.Entity<EfItem>().ToTable("EFItem");

EF follows some naming convention between C# class and DB table name, since you have created a class named 'Item', i am afraid EF mixed class Item and EFItem together.

like image 138
Yang You Avatar answered Nov 24 '25 21:11

Yang You



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!