Extending DbContext from another project

asp.net asp.net-mvc-5 c# entity-framework-6

Question

I am trying to achieve some layer separation with a project that I am working on. The idea is that I can take any feature the website has and modulize it. For example, if you take something such as News and you wish to add the feature to the website, then in the code you would just need to import the news service and the models, data layer, etc.. all get imported with it.

Note that I am using Ninject as part of project to inject dependencies so that I do not need to reference concrete classes, I only reference contracts.

I have my main project and the data Layer as such:

public class MainDbContext : CustomIdentityDbContext, IMainDbContext
{

}

As you can see it extends CustomIdentityDbContext which looks as this:

public class CustomIdentityDbContext : IdentityDbContext<CustomUser>, ICustomIdentityDbContext
{
    public CustomIdentityDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaim");
        modelBuilder.Entity<IdentityUserRole>().ToTable("UserRole");
        modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogin");
        modelBuilder.Entity<IdentityRole>().ToTable("Role");
        modelBuilder.Entity<AzularisUser>().ToTable("User");
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    }

    public static DbContext Create()
    {
        return new CustomIdentityDbContext();
    }
}

My problem is that I need to have a News table and then I need EF6 to map that table as it does for Identity. What I can't figure out is how to create this relationship. How do I add DbSet<News> News { get; set; } to my MainDbContext without ever having to write that line in there and reference News object. So I am trying to achieve it dynamically being added to my MainDbContext Class the moment I reference my service as that service will trigger all the other classes into creating themselves via ninject and binding.

In other words, A Data Layer for News needs to inject itself into MainDbContext without MainDbContext knowing about it in its references. Is something like this possible? If so how would I achieve it?

Here is what I tried:

public class NewsDbContext : DbContext, INewsDbContext
{
    public DbSet<News> News { get; set; }
    public NewsDbContext() : base("DefaultConnection") { }

    public IEnumerable<INews> QueryNews()
    {
        return News;
    }
}

The above code compiles and runs, however, it does not create a table and when I try to query it or insert a record, it will break as the table doesn't exist. I'm not quite sure how to accomplish this and I'm very much out of ideas. Is something like this possible?

EDIT:

I realised I may not have explained myself properly. Let me try again. I have a multi layered structure. I have a Service, Data, Domain, and DTO Layers. These 4 layers compose a plugin. On top of these 4 layers, I have 4 more that are interfaces of these layers, so in other words contracts. I use Ninject, to bind my contracts to implementation layers so that the main project never actually references the implementation layers in the project. For this reason I am attempting to plug in the data layer into the main project's data layer, without main project data layer actually ever referencing the plugin's data layer. The problem is that the plugin will have its own needed tables. I would like these tables to be automatically created with EF6 the first time project is run.

My main project is also split up in the same way as a plugin, it has all 4 layers, plus a Manager Layer. The manager is a collection of services and serves no other purpose, it just instantiates them based on contract with help of Ninject. The core of the project holds all the views, scripts and css files, but it references the interface of the manager. With ninject, I create an instance of the manager. The manager again has all the references to the contracts and with ninject it creates instances of those contracts. The Services reference the contracts of Data layer and the data layer will query the information and bring back results to the service which will get passed back to the controller. I hope this explains my structure a little better. With this approach I am attempting to create a trully modular project where you can swap implementations of a dll and have the project continue working, no need to recompile the whole darn thing. I can have two completely sepparate functionalities on 2 dll's, but as long as they implement the same contract, the website should work regardless of which dll it has in its folder.

Everything I just described works with the exception of making it work with EF6. I have a very similar structure in place with DAPPER and there its all nice and dandy as you write your own query and such so I have had no issues. I am trying to replicate this with EF6. So again, my question is, how do I get EF6 to create the tables, how do I add them dynamically in when the only thing that gets referenced is the service interface. Maybe the interface can have a MapTables function, but again I'm not sure how to accomplish the dynamic adding to EF6.

An idea I had, and I'm not sure if this can work nor how I would do it, but what if I pass the DB context as a type instead of extending it?

1
2
9/6/2016 3:15:26 PM

Popular Answer

I have partially resolved my problem. I will post this as an answer and update once I have a full solution in place. So far I have learned how to get my tables generated. It is with the following code inside DB Context of the main project:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");

    foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
        var entityTypes = assembly
          .GetTypes()
          .Where(t =>
            t.GetCustomAttributes(typeof(PersistentAttribute), inherit: true)
            .Any());

        foreach (var type in entityTypes)
        {
            entityMethod.MakeGenericMethod(type)
              .Invoke(modelBuilder, new object[] { });
        }
    }

    base.OnModelCreating(modelBuilder);
}

The above code will ensure that the tables are created for any class that has "PersistentAttribute" added. My next problem is trying to query from the external DLL. So the Data Layer of News Project needs to be able to query itself by having the context somehow passed that is created in the main project. I have few ideas on that thanks to ninject, I will post more once I have it figured out.

1
9/8/2016 8:12:23 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow