1

I'm facing a puzzle regarding the entity framework. Using mvc 4 and entity framework 5.0, I am developing an addon to an existing application which has the following table names:

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

..

A user generally only has access to either company one or company two, and I want to support users of all companies. This calls for a generic approach.

So I created two edmx files, one for company 1, and one for company 2, using the same entity and context names. Resulting in classes such as

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

and two context classes:

Entities.Company1.Company1Entities
Entities.Company2.Company2Entities

This is not enough to be able to use it in a generic way because for instance, the controller would need a repository variable such as:

private Company1Entities db = new Company1Entities()

and for company 2 this would be

private Company2Entities db = new Company2Entities()

The generic approach would be to use an interface ICompanyEntities and a factory to get the correct repository. BUT - the contained DbSet properties in the context classes may have the same name, they are not of the same type. To be specific, for instance the products set in the context classes is now defined as

DbSet<Entities.Company1.Product> Products  {}

versus

DbSet<Entities.Company2.Product> Products  {}

So I modified the T4 templates to generate an interface for each type, have each entity implement that interface, and generate each context class as containing the dbset with that interface, eg

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

for each company:

public class Product : IProduct {} 

and in the context classes:

public class Company1Entities : DbContext, IRepository 
{
    ...

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

This compiled fine, and I was hopeful the problem was solved. But the entity framework choked on it big time so I had to roll back entirely.

I then tried to use use the dynamic keyword for the db variable but linq won't accept that. Can someone explain me how to solve this issue? I am starting to feel that this is not possible with the entity framework, unless I write my controllers as partials and implement one controller for each company, containing only the line which declares the db variable. Which is something I really don't want to do, I'd rather duplicate the controller class entirely. Is there a solution, a generic approach? Am I missing something here? Help would be greatly appreciated.

2
  • This is not enough to be able to use it in a generic way - not really, those XXXEntities classes derive from System.Data.Entities.ObjectContext, therefore you could declare a member of that type and reference it. Commented Feb 3, 2013 at 21:13
  • yes, but that would not provide me with the correct DbSet properties. Commented Feb 3, 2013 at 21:33

2 Answers 2

1

Because they're both the same exact model, you can use the same context for both. Just load the data differently depending on the customer. Take a value in the constructor indicating the table prefix, then override the OnModelCreating method to set that prefix on the table mappings.

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");
    }
}

IMPORTANT EDIT: The default configuration will cache your model on the first creation of GenericEntities in your app, and you'll never get two versions of the model created. The easiest/quickest way around this would be to create a subclass of GenericEntities for each client so they each cache separately. Long term with many clients, you would need to implement your own caching scheme (based on table prefix instead of entity class type).

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

public class Customer2Entities : GenericEntities
{
    public Customer2Entities() : base("customer2") {}
}
Sign up to request clarification or add additional context in comments.

4 Comments

When does OnModelCreating run? Each time GenericEntities is instantiated? Or just once?
@usr By default, it's called on the first instantiation of GenericEntities and then cached. You can disable caching with modelBuilder.ModelCaching = false, or write a custom model builder with custom caching. I'll update the answer to reflect this.
@gd73 it is for code-first (ie DbContext) only
it strangely seems to work for some entities, but not for others. investigating ..
0

The answer from just.another.programmer was correct, but did not apply to my situation as I am using database-first. Instead, I now use T4 to generate generic entities, for each entity and company I create code such as

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

and this solved most of my issues.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.