2

如果在实体框架中使用 TPH 给定表结构。

class ContactLink {
    Guid Contact_Link_ID { get; set;} //pk
    Guid Tenant_ID { get; set;} //fk
    Guid Contact_ID { get; set;} //fk
}

class ContactLinkCustomer : ContactLink {
    Guid Contact_Link_ID { get; set;} //fk
    Guid Customer_ID { get; set;} //fk
}

由于实体框架不包含派生类表中的基类属性,我应该如何为拆分合并操作配置弹性比例架构信息?特别是 Tenant_ID,这是我的点图分片键。

SchemaInfo schemaInfo = new SchemaInfo();
schemaInfo.Add(new ShardedTableInfo("dbo", "ContactLinkCustomer", ???));
smm.GetSchemaInfoCollection().Add("ShardName", schemaInfo);

更新: ContactLink 不是抽象的。

更新 2: 我应该注意到 ContactLink 也在我的 DbContext 中,并且独立于 ContactLinkCustomer 进行查询。

更新 3: 我没有使用 TPH,我们实际上使用的是 TPT。这就是导致多个表而不是带有鉴别器的单个表的原因。

4

2 回答 2

1

如果您使用的是 TPH 并且ContactLinkContactLinkCustomer都在同一个层次结构中,那么 EF 应该已经创建了一个包含两个类的所有列的非规范化表。在这种情况下,ContactLink 将是以 Tenant_ID 作为分片键的分片表。

但是,如果您真的打算使用多个表,则必须在表中为ContactLinkCustomer包含 Tenant_ID 列,并将其分片到 Tenant_ID。当前版本的 Elastic Sc​​ale 库和工具要求分片键存在于参与拆分合并的所有分片表中。

于 2015-06-17T17:31:05.877 回答
1

下面对我有用,但需要注意的是,没有数据库级别的约束可以使 Tenant_ID 保持同步,因此如果任何代码直接通过 T-SQL(而不是通过 EF)修改这些表,它们可能会不同步。

[Table("ContactLink")] // TPT inheritance
class ContactLink
{
    public Guid Contact_Link_ID { get; set; } //pk
    public Guid Tenant_ID { get; set; } //fk
    public Guid Contact_ID { get; set; } //fk
}

[Table("ContactLinkCustomer")] // TPT inheritance
internal class ContactLinkCustomer : ContactLink
{
    // Dummy property to trick EF into creating it as a column for sharding purposes
    // Callers should just directly use the base Tenant_ID property
    // It would be nice if we could set this to be public/protected, but then EF
    // won't create it as a column. Maybe there is a workaround for this?
    [Column("Tenant_ID")]
    public Guid Tenant_ID_ContactLinkCustomer
    {
        get { return base.Tenant_ID; }
        set { base.Tenant_ID = value; }
    }

    public Guid Contact_Link_ID { get; set; } //fk
    public Guid Customer_ID { get; set; } //fk
}

SSMS 截图

我用于测试的其他类如下。

class Program
{
    static void Main(string[] args)
    {
        string connStr = "Server=(local);Database=EfShardingTpt;Integrated Security=true";

        using (MyDbContext myDbContext = new MyDbContext(connStr))
        {
            // Drop and recreate database
            Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());
        }

        // Create ContactLinkCustomer
        using (MyDbContext myDbContext = new MyDbContext(connStr))
        {
            ContactLinkCustomer clc = new ContactLinkCustomer
            {
                Contact_ID = Guid.Empty,
                Contact_Link_ID = Guid.Empty,
                Customer_ID = Guid.Empty,
                Tenant_ID = Guid.Parse("00000000-0000-0000-0000-100000000000")
            };

            myDbContext.ContactLinkCustomers.Add(clc);
            myDbContext.SaveChanges();
        }

        WriteTenantIds(connStr);

        // Update through subtype
        using (MyDbContext myDbContext = new MyDbContext(connStr))
        {
            ContactLinkCustomer clc = myDbContext.ContactLinkCustomers.First();
            clc.Tenant_ID = Guid.Parse("00000000-0000-0000-0000-200000000000");
            myDbContext.SaveChanges();
        }

        WriteTenantIds(connStr);

        // Update through supertype
        using (MyDbContext myDbContext = new MyDbContext(connStr))
        {
            ContactLink cl = myDbContext.ContactLinks.First();
            cl.Tenant_ID = Guid.Parse("00000000-0000-0000-0000-300000000000");
            myDbContext.SaveChanges();
        }

        WriteTenantIds(connStr);
    }

    private static void WriteTenantIds(string connectionString)
    {
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            SqlCommand cmd = conn.CreateCommand();

            cmd.CommandText = "SELECT Tenant_ID FROM ContactLink";
            Guid contactLinkTenantId = (Guid) cmd.ExecuteScalar();

            cmd.CommandText = "SELECT Tenant_ID FROM ContactLinkCustomer";
            Guid contactLinkCustomerTenantId = (Guid)cmd.ExecuteScalar();

            Console.WriteLine("{0} {1}", contactLinkTenantId, contactLinkCustomerTenantId);
        }
    }
}

class MyDbContext : DbContext
{
    public MyDbContext(string connectionString) : base(connectionString)
    {
    }

    public virtual DbSet<ContactLink> ContactLinks { get; set; }
    public virtual DbSet<ContactLinkCustomer> ContactLinkCustomers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ContactLink>()
            .HasKey(e => e.Contact_Link_ID);

        modelBuilder.Entity<ContactLinkCustomer>()
            .HasKey(e => e.Contact_Link_ID);
    }
}

控制台输出:

00000000-0000-0000-0000-100000000000 00000000-0000-0000-0000-100000000000
00000000-0000-0000-0000-200000000000 00000000-0000-0000-0000-200000000000
00000000-0000-0000-0000-300000000000 00000000-0000-0000-0000-300000000000

也可能存在某种基于映射的解决方案。我尝试了以下方法,但它不起作用。也许通过更多的实验它可以工作,但上述解决方案对我来说似乎已经足够好了,所以我没有进一步探索它。

modelBuilder.Entity<ContactLinkCustomer>()
            .Map(m =>
            {
                m.Properties(e => e.Tenant_ID);
                m.ToTable("ContactLinkCustomer");
            });

Unhandled Exception: System.NotSupportedException: The type 'ContactLinkCustomer' cannot be mapped as defined because it maps inherited properties from types th
at use entity splitting or another form of inheritance. Either choose a different inheritance mapping strategy so as to not map inherited properties, or change
all types in the hierarchy to map inherited properties and to not use splitting.
于 2015-06-17T19:26:18.760 回答