1

我正面临有关实体框架的难题。使用 mvc 4 和实体框架 5.0,我正在开发一个现有应用程序的插件,该应用程序具有以下表名:

dbo.company1_contact
dbo.company1_product
dbo.company2_contact
dbo.company2_product

..

一个用户通常只能访问公司一或公司二,我想支持所有公司的用户。这需要一种通用的方法。

所以我创建了两个 edmx 文件,一个用于公司 1,一个用于公司 2,使用相同的实体和上下文名称。导致类如

Entities.Company1.Contact
Entities.Company1.Product
Entities.Company2.Contact
Entities.Company2.Product

和两个上下文类:

Entities.Company1.Company1Entities
Entities.Company2.Company2Entities

这不足以以通用方式使用它,因为例如,控制器需要一个存储库变量,例如:

private Company1Entities db = new Company1Entities()

对于公司 2,这将是

private Company2Entities db = new Company2Entities()

通用方法是使用接口 ICompanyEntities 和工厂来获取正确的存储库。但是 - 上下文类中包含的 DbSet 属性可能具有相同的名称,它们的类型不同。具体来说,例如在上下文类中设置的产品现在定义为

DbSet<Entities.Company1.Product> Products  {}

相对

DbSet<Entities.Company2.Product> Products  {}

所以我修改了 T4 模板为每种类型生成一个接口,让每个实体实现该接口,并生成每个上下文类作为包含具有该接口的 dbset,例如

public interface IRepository {} 
public interface IContact {} 
public interface IProduct {} 

对于每家公司:

public class Product : IProduct {} 

在上下文类中:

public class Company1Entities : DbContext, IRepository 
{
    ...

    public DbSet<IProduct> Products { get; set; }
    public DbSet<IContact> Contacts { get; set; }
}

这编译得很好,我希望问题得到解决。但是实体框架严重阻塞了它,所以我不得不完全回滚。

然后我尝试对 db 变量使用 dynamic 关键字,但 linq 不会接受。有人可以解释我如何解决这个问题吗?我开始觉得实体框架不可能做到这一点,除非我将控制器编写为部分控制器并为每个公司实现一个控制器,只包含声明 db 变量的行。这是我真的不想做的事情,我宁愿完全复制控制器类。有解决方案,通用方法吗?我在这里错过了什么吗?帮助将不胜感激。

4

2 回答 2

1

因为它们都是完全相同的模型,所以您可以对两者使用相同的上下文。只需根据客户的不同加载数据。在构造函数中取一个指示表前缀的值,然后覆盖该OnModelCreating方法以在表映射上设置该前缀。

public class GenericEntities : DbContext, IRepository 
{
    //...

    private string TablePrefix { get; set; }

    public GenericEntities(string tablePrefix)
    {
        this.TablePrefix = tablePrefix;
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Contact> Contacts { get; set; }

    public override OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>.ToTable(TablePrefix + "_product");
        modelBuilder.Entity<Customer>.ToTable(TablePrefix + "_customer");
    }
}

重要编辑: 默认配置将在您的应用程序中第一次创建 GenericEntities 时缓存您的模型,并且您永远不会创建模型的两个版本。解决这个问题的最简单/最快的方法是为每个客户端创建一个 GenericEntities 的子类,以便它们分别缓存。长期使用许多客户端,您需要实现自己的缓存方案(基于表前缀而不是实体类类型)。

public class Customer1Entities : GenericEntities
{
    public Customer1Entities() : base("customer1") {}
}

public class Customer2Entities : GenericEntities
{
    public Customer2Entities() : base("customer2") {}
}
于 2013-02-03T21:22:43.317 回答
0

just.another.programmer 的答案是正确的,但不适用于我的情况,因为我使用的是数据库优先。相反,我现在使用 T4 生成通用实体,为每个实体和公司创建代码,例如

IQueryable<GenericCustomer> Customers 
{
    get
    {
        return from c in db.Customers1
                select new GenericCustomer() 
                { Name = c.Name, ... }
    }
}

这解决了我的大部分问题。

于 2013-02-04T22:03:39.767 回答