2016-08-12 90 views
2

我有一个简单DbContext看起来像:的DbContext和范围依赖

public class MyDbContext : DbContext 
{ 
    private readonly IUserContext _userContext; 

    public MyDbContext(IUserContext userContext) : base("DefaultConnectionString") 
    { 
     _userContext = userContext; 

     Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>()); 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     // ... Here I need to creates some filters using the IUserContext dependency 

     base.OnModelCreating(modelBuilder); 
    } 

} 

DbContext使用Func<T>工厂,using the guidelines in the Simple Injector documentation有线:container.RegisterFuncFactory<DbContext, MyDbContext>(Lifestyle.Scoped);

public static void RegisterFuncFactory<TService, TImpl>(
    this Container container, Lifestyle lifestyle = null) 
    where TService : class 
    where TImpl : class, TService 
{ 
    lifestyle = lifestyle ?? Lifestyle.Transient; 
    var producer = lifestyle.CreateProducer<TService, TImpl>(container); 
    container.RegisterSingleton<Func<TService>>(producer.GetInstance); 
} 

但apperently,这种简单的情况下是不可能的DbContext因为此消息:

目标上下文'MyDbContext'不可构造。添加一个默认的 构造函数或提供IDbContextFactory的实现。

我真的不喜欢的IDbContextFactory的想法,所以唯一的解决办法,我能想出是去除依赖于MyDbContext,将其设置为一个属性,修改RegisterFuncFactory方法和手动初始化的背景:

internal static void RegisterFuncFactory<TService, TImpl>(this Container container, Func<TImpl> instanceProducer, Lifestyle lifestyle = null) where TService : class where TImpl : class, TService 
{ 
    lifestyle = lifestyle ?? Lifestyle.Transient; 
    var producer = lifestyle.CreateProducer<TService>(instanceProducer, container); 
    container.Register<Func<TService>>(() => producer.GetInstance, Lifestyle.Singleton); 
} 

container.RegisterFuncFactory<DbContext, MyDbContext>(() => new MyDbContext 
{ 
    UserContext = container.GetInstance<IUserContext>() 
}, Lifestyle.Scoped); 

尽管它不够优雅,但是还有其他更好的方法来满足我的需求吗?我喜欢明确依赖上下文,但似乎不可能。

UPDATE

错误是来自:

'System.Data.Entity.Migrations.Infrastructure.MigrationsException' 发生在EntityFramework.dll但在用户代码

没有处理

在此代码查询方法这里的return语句:

internal sealed class EntityFrameworkRepository<TEntity> : IEntityWriter<TEntity>, IEntityReader<TEntity> where TEntity : Entity 
{ 
    private readonly Func<DbContext> _contextProvider; 

    public EntityFrameworkRepository(Func<DbContext> contextProvider) 
    { 
     _contextProvider = contextProvider; 
    } 

    public IQueryable<TEntity> Query() 
    { 
     var context = _contextProvider(); 
     return context.Set<TEntity>().AsNoTracking(); 
    } 

    // Methods removed for brevity 

} 
+0

谁给你这个消息:

public class MyDbContext : DbContext { private readonly IUserContext _userContext; // For migrations public MyDbContext() : base("DefaultConnectionString") { Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>()); } // For applications public MyDbContext(IUserContext userContext) : base("DefaultConnectionString") { _userContext = userContext; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // ... Code removed for brevity base.OnModelCreating(modelBuilder); } } 

这是然后在根组成像有线? EF迁移? – Steven

+0

是的,错误来自EntityFramework中发生的'System.Data.Entity.Migrations.Infrastructure.MigrationsException'。dll,但没有在用户代码中处理返回statemenet在这里:var context = _contextProvider();返回context.Set ().AsNoTracking();' – janhartmann

+0

@janhartmann什么是实体(约束)?我认为你也应该指定一个'class'约束,因为现在'TEntity'可能是一个不允许的接口 –

回答

2

添加第二个(默认)构造函数。这样,EF迁移可以在从命令行运行时使用此构造函数,而您可以让您的应用程序使用第二个构造函数。

当您添加第二个构造函数时,您在DbContext上松动了简单喷射器的自动布线功能,但这不应该是一个问题;你可以简单地连接你的上下文,如下所示:

IUserContext userContext = new AspNetUserContext(); 

container.RegisterSingleton<IUserContext>(userContext); 

var contextProducer = Lifestyle.Scoped.CreateProducer<DbContext>(
    () => new MyDbContext(userContext), 
    container); 

container.RegisterSingleton<Func<DbContext>>(contextProducer.GetInstance); 
+0

谢谢Steven,要试试这个。我不得不将单身人士注册编号改为:'container.RegisterSingleton >(()=> contextProducer.GetInstance);' – janhartmann

+0

像魅力一样工作!我会将您的答案标记为解决方案,我会为其他人发布完整解决方案的答案(或者这是不允许的?)。 – janhartmann

+0

@janhartmann:我认为发布完整的解决方案是一个很好的做法。更多的人应该这样做。 – Steven

0

这个答案只是为了向更多的用户展示我结束了什么。 @Steven的答案是正确的答案。

为了能够在支持迁移时将相关性注入到DbContext中,我们必须使用两个构造函数。一个用于迁移,一个用于应用程序。

public static void RegisterEntityFramework<TContext>(this Container container, Func<TContext> context) where TContext : DbContext 
{ 
    if (container == null) throw new ArgumentNullException(nameof(container)); 

    var contextProducer = Lifestyle.Scoped.CreateProducer<DbContext>(context, container); 
    container.RegisterSingleton<Func<DbContext>>(() => contextProducer.GetInstance); 
} 

var userContext = new AspNetHttpUserContext(); 
var container = new Container(); 
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle(); 
container.RegisterSingleton<IUserContext>(userContext); 
container.RegisterEntityFramework(() => new WayFinderDbContext(userContext)); 
container.Verify();