EntityFramework, Azure ElasticScale и наследование таблиц по типам (TPT)
Если задана структура таблицы с использованием TPH в Entity Framework.
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
}
Как мне сконфигурировать информацию схемы эластичного масштаба для операций слияния с разделением, поскольку платформа Entity не включает свойства базового класса в таблицу производного класса? В частности, 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. Что и послужило причиной появления нескольких таблиц вместо одной таблицы с дискриминатором.
2 ответа
Ниже работает для меня, с оговоркой, что нет ограничения уровня базы данных, которое поддерживает 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
}
Дополнительные классы, которые я использовал для тестирования, приведены ниже.
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.
Если вы используете TPH и оба ContactLink и ContactLinkCustomer находятся в одной и той же иерархии, то EF должен был создать одну денормализованную таблицу со всеми столбцами из обоих классов. В этом случае ContactLink будет зашитой таблицей с Tenant_ID в качестве ключа.
Однако, если вы действительно планируете работать с несколькими таблицами, вы должны включить столбец Tenant_ID в таблицу для ContactLinkCustomer и добавить его в Tenant_ID. Текущие версии библиотек и инструментов Elastic Scale требуют, чтобы ключ разделения присутствовал во всех разделенных таблицах, участвующих в Split-Merge.