Load EF Core 2 DbContext from dynamically imported DLL

asp.net-core-2.0 c# ef-core-2.0 entity-framework-core system.reflection

Question

I have a DbContext I need to dynamically loading from a DLL as the Db context will change and need to be edited while the application is running, however I can't seem to create an instance of this context as I appear to need to pass in the context options. this code is below.

Assembly reporting = Assembly.LoadFile(pathString);

        var defaultContext = reporting.GetType("Reporting.DefaultContext");
        var dbContext = Activator.CreateInstance(defaultContext, new
 object[] { /*need to pass in the contructor here*/ }) as DbContext;

and my compiled context is pretty standard, a modified version of this below for reference

public class DefaultContext : DbContext
{
    public DefaultContext(DbContextOptions<DefaultContext> options)
        : base(options)
    { }

    public virtual DbSet<Student> Students { get; set; }
    public virtual DbSet<Course> Courses { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student>().ToTable("Students ", "core")
            .AllProperties().ForEach(x => x.IsRequired());

        modelBuilder.Entity<Course>().ToTable("Courses", "core")
            .AllProperties().ForEach(x => x.IsRequired());
    }
}

I'm pretty inexperienced when it comes to reflection - so questions really are;

Is this the best way to try and achieve this or should I be creating a method to invoke on the DLL to return the context?

If not how do I go about passing through an instance of DbContextOptions when I don't have the context?

Side note, this is an Asp.Net Core 2.0 MVC project.

EDIT: when instead passing an empty obj array into create instance as below I get a missing method exception (constructor not found)

var dbContext = Activator.CreateInstance(defaultContext, new object[] { new object[0] }) as DbContext;

Whereas if I don't pass in the second obj parameter I get the same exception but with message "No parameterless constructor defined for this object" so now I'm pretty lost as there's clearly a construtor.

1
1
5/30/2018 1:45:16 PM

Accepted Answer

Basically the question is how to create / get DbContextOptions<DefaultContext> instance via reflection. You can do that by first constructing a generic type via MakeGenericType and then use that type as argument to Activator.CreateInstance.

So having

var dbContextType = reporting.GetType("Reporting.DefaultContext");

something like this:

var optionsType = typeof(DbContextOptions<>).MakeGenericType(dbContextType);
var options = (DbContextOptions)Activator.CreateInstance(optionsType);
var dbContext = (DbContext)Activator.CreateInstance(dbContextType, options);

However, the whole purpose of the DbContextOptions<TDbContext> options constructor argument is to allow configuring the db context (like database provider, connection string etc.) from outside, rather than self-configuring inside OnModelConfiguring, in which case your derived db context would simply have parameterless constructor.

Hence it would be better to create DbContextOptionsBuilder<DefaultContext instance, use Fluent APi to configure it, and then pass Options property (which is the intended DbContextOptions<DefaultContext type) to the DefaultContext constructor. E.g.

var optionsBuilderType = typeof(DbContextOptionsBuilder<>).MakeGenericType(dbContextType);
var optionsBuilder = (DbContextOptionsBuilder)Activator.CreateInstance(optionsBuilderType);

string connectionString = ...;
optionsBuilder.UseSqlServer(connectionString);

var dbContext = (DbContext)Activator.CreateInstance(dbContextType, optionsBuilder.Options);
3
5/30/2018 12:20:15 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