实体框架多租户共享数据架构:单列,多个外键

时间:2012-08-14 09:48:47

标签: c# entity-framework data-modeling multi-tenant

我有以下数据结构:

//property Notification
abstract class BindableBase { }
//base class for all tenant-scoped objects
abstract class TenantModelBase : BindableBase 
{ 
  int TenantId;
} 

abstract class Order : TenantModelBase 
{
   Customer Customer; //works: mapped using TenantId and CustomerId
   Product Product; //again, works with TenantId and ProductId
   string ProductId;
   string CustomerId;
}
class Customer: TenantModelBase 
{
   string CustomerId; 
}

class Product  : TenantModelBase 
{
   string ProductId;
}

class SpecialOrder : Order
{
    OtherClass OtherClass; //this fails!, see below
    string OtherClassId;
}
class SuperSpecialOrder : SpecialOrder {  }

class OtherClass  : TenantModelBase 
{
    string OtherClassId;
}

我收到以下错误:

  

外键组件'TenantId'不是声明的属性   输入'SpecialOrder'。验证是否未明确排除它   来自模型,它是一个有效的原始属性。

使用Fluent Api配置发生错误:

        config.HasRequired(p => p.OtherClass)
            .WithMany(oc => oc.SpecialOrders)
            .HasForeignKey(p => new {  p.TenantId, p.OtherClassId});

如果OtherClass中没有SpecialOrder引用,我可以毫无问题地自由创建对象(包括SpecialOrderSuperSpecialOrder等)。

任何人都知道发生了什么事?我迷失在这里:(

修改 我在其他问题中看到人们从表中删除了TenantId,这不是一个选项,因为主键在租户中不是唯一的,我们希望保留共享数据架构。

我知道解决方法是在SpecialOrder类中有第二个TenantId,但这对我来说似乎不合逻辑。

2 个答案:

答案 0 :(得分:7)

您是否正在尝试进行TPT,其中有一个单独的订单/租户表?如果是这样,我认为另一张海报是正确的,EF中有一个错误。

如果客户/产品是基于您的映射类,这可能对您有用。

我在创建数据库时发生了什么事情,它试图映射抽象的Order类。

添加:

modelBuilder.Ignore<Order>();

构建器由于MapInheritedProperties而保留了映射的属性,但没有创建表,因此正确创建了所有FK。

我假设您希望为每个类创建单独的表,例如上面的相关帖子,并且不要映射您的抽象表。

整个模型:

 public abstract class BindableBase { }
   //base class for all tenant-scoped objects
   public abstract class TenantModelBase : BindableBase
   {
      [Key]
      public virtual int TenantId { get; set; }
   }

   public abstract class Order : TenantModelBase
   {
      public Customer Customer { get; set; } //works: mapped using TenantId and CustomerId
      public Product Product { get; set; } //again, works with TenantId and ProductId
      public string ProductId { get; set; }
      public string CustomerId { get; set; }
   }
   public class Customer : TenantModelBase
   {
      [Key]
      public string CustomerId { get; set; }
   }

   public class Product : TenantModelBase
   {
      [Key]
      public string ProductId { get; set; }
   }

   public class SpecialOrder : Order
   {
      [Key]
      public int SpecialOrderId { get; set; }
      public OtherClass OtherClass { get; set; } //this fails!, see below
      public string OtherClassId { get; set; }
   }
   public class SuperSpecialOrder : SpecialOrder { }

   public class OtherClass : TenantModelBase
   {
      public string OtherClassId { get; set; }
      public ICollection<SpecialOrder> SpecialOrders { get; set; }
   }



   public class Model : DbContext
   {
      public DbSet<Customer> Customers { get; set; }
      public DbSet<Product> Products { get; set; }
      public DbSet<SpecialOrder> SpecialOrders { get; set; }
      public DbSet<SuperSpecialOrder> SuperSpecialOrders { get; set; }

      public DbSet<OtherClass> OtherClasses { get; set; }

      protected override void OnModelCreating( DbModelBuilder modelBuilder )
      {
         modelBuilder.Entity<OtherClass>()
            .HasKey( k => new { k.TenantId, k.OtherClassId } );

         modelBuilder.Entity<Customer>()
            .HasKey( k => new { k.TenantId, k.CustomerId } );

         modelBuilder.Entity<Product>()
            .HasKey( k => new { k.TenantId, k.ProductId } );


         modelBuilder.Entity<SpecialOrder>()
            .Map( m =>
                     {
                        m.MapInheritedProperties();
                        m.ToTable( "SpecialOrders" );
                     } );

         modelBuilder.Entity<SpecialOrder>().HasKey( k => new { k.TenantId, k.SpecialOrderId } );

         modelBuilder.Entity<SuperSpecialOrder>()
          .Map( m =>
          {
             m.MapInheritedProperties();
             m.ToTable( "SuperSpecialOrders" );
          } )
          .HasKey( k => new { k.TenantId, k.SpecialOrderId } );

         modelBuilder.Entity<SpecialOrder>()
          .HasRequired( p => p.OtherClass )
          .WithMany( o => o.SpecialOrders )
          .HasForeignKey( p => new { p.TenantId, p.OtherClassId } );

         modelBuilder.Entity<Order>()
            .HasRequired( o => o.Customer )
            .WithMany()
            .HasForeignKey( k => new { k.TenantId, k.CustomerId } );

         modelBuilder.Entity<Order>()
          .HasRequired( o => o.Product )
          .WithMany()
          .HasForeignKey( k => new { k.TenantId, k.ProductId } );

         modelBuilder.Ignore<Order>();


      }
   }

创建的数据库: enter image description here

希望这有帮助。

答案 1 :(得分:0)

我想在这里猜一下,因为我看到Julie Lermann在ef4.1博客中发现的一个不寻常的错误 查询中的命名空间导致了一个问题。

只是为了快速测试这个bug是否是您的问题,将所有对象的命名空间更改为相同。      命名空间xyz //与dbcontext和实体相同     公共类OtherClass {}

快速测试。如果不是,抱歉浪费你的时间。