Home > Blockchain >  DbContextFactory in WPF .Net 6 project (EF, MVVM)
DbContextFactory in WPF .Net 6 project (EF, MVVM)

Time:10-18

My application is working fine when I use a plain/classic DBContext implementation, but when I try DbContextFactory, _contextFactory.CreateDbContext() is always failing with 'null' exception. What am I missing?

My App.xaml.cs (no changes were needed in this file whilst using DBContext):

private void ConfigureServices(IServiceCollection services)
{
    string defaultConnection = Settings.Default.DefaultConnection;

    services.AddDbContextFactory<MyDbContext>(options => options.UseMySql(defaultConnection, ServerVersion.AutoDetect(defaultConnection)));

    services.AddTransient(typeof(MainWindow));
}

MyDbContext.cs file (no changes were needed as it seems to match DbContextFactory constructor's requirements already):

public class MyDbContext : DbContext
{
    public MyDbContext (DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }
    
    // DbSets 

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            string defaultConnection = Settings.Default.DefaultConnection;

            var options = new DbContextOptionsBuilder<MyDbContext>()
                            .UseMySql(defaultConnection, ServerVersion.AutoDetect(defaultConnection))
                            .Options;
        }

        optionsBuilder.UseLazyLoadingProxies();
        // To be disabled in production 
        optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
        optionsBuilder.EnableSensitiveDataLogging();
        optionsBuilder.EnableDetailedErrors();
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Table building logic for EF code-first
    }
}

MainWindow.xaml.cs file:

public partial class MainWindow : Window
{
    private readonly IDbContextFactory<MyDbContext> _contextFactory;

    private SomeVieModel _someVieModel;

    public MainWindow()
    {
        InitializeComponent();

        var _context = _contextFactory.CreateDbContext(); // Throws 'null' exception

        // Probably should be instantiating a new DbContext 
        // in the VM itself, instead of passing it on? 
        _someVieModel = new SomeVieModel(_context); 
    }
}

I've checked numerous Blazor examples, because of the lack of WPF ones, and I feel I'm missing something very simple, some one line of DbContextFactory object instantiation? Like in this example - where is IDbContextFactory<MyDbContext> contextFactory object coming from and where is it instantiated? Thank you!

CodePudding user response:

I think I've worked it out, although I'm sure some of you will be pointing out the error of my ways :)

I just realised, that I've already had my own DbContextFactory class created for database migrations, because otherwise the EF Core could not connect to the database via project's DbContext class alone.

public class MyDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
    public MyDbContext CreateDbContext(string[]? args = null)
    {
        string defaultConnection = Settings.Default.DefaultConnection;

        var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
        optionsBuilder.UseMySql(defaultConnection, ServerVersion.AutoDetect(defaultConnection));

        return new MyDbContext(optionsBuilder.Options);
    }
}

I've commented the code in App.xaml.cs out and initialised DbContextFactory via my own class instead of IDbContextFactory interface:

public partial class MainWindow : Window
{
    private readonly MyDbContextFactory  _contextFactory;

    private SomeVieModel _someVieModel;

    public MainWindow()
    {
        InitializeComponent();

        _contextFactory = new MyDbContextFactory();

        _someVieModel = new SomeVieModel(_contextFactory); 
    }
}

And called CreateDbContext() in a view model:

public class SomeVieModel : ViewModelBase
{
    private readonly MyDbContextFactory _contextFactory;

    public SomeVieModel(MyDbContextFactory contextFactory)
    {
        _contextFactory = contextFactory; 
        
        await LoadDBStuff();
    }
    
    private async Task LoadDBStuff()
    {
        using (var context = _contextFactory.CreateDbContext())
        {
            await context.SomeDataModel.LoadAsync();
            SomeDataModelObservableCollection = context.SomeDataModel.Local.ToObservableCollection();
        }
    }
}
  • Related