C# DbContext和作用域依赖项

C# DbContext和作用域依赖项,c#,entity-framework,entity-framework-6,simple-injector,C#,Entity Framework,Entity Framework 6,Simple Injector,我有一个简单的DbContext看起来像: public class MyDbContext : DbContext { private readonly IUserContext _userContext; public MyDbContext(IUserContext userContext) : base("DefaultConnectionString") { _userContext = userContext; Databa

我有一个简单的
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
是不可能的,因为这样的消息:

目标上下文“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);
internal static void RegisterFuncFactory(此容器容器,Func instanceProducer,lifety Lifestyle=null),其中TService:class,其中TImpl:class,TService
{
生活方式=生活方式??生活方式。短暂;
var producer=lifety.CreateProducer(instanceProducer,容器);
container.Register(()=>producer.GetInstance,lifety.Singleton);
}
container.RegisterFuncFactory(()=>newmydbcontext
{
UserContext=container.GetInstance()
},生活方式。范围);
虽然它并不优雅,但是否有另一种“更好”的方式来满足我的需求?我喜欢对上下文的依赖,但似乎不可能

更新

错误来自:

'System.Data.Entity.Migrations.Infrastructure.MigrationsException' 在EntityFramework.dll中发生,但未在用户代码中处理

在此代码中,查询方法的返回语句如下:

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

}
内部密封类EntityFrameworkRepository:EntityWriter,EntityReader其中tenty:Entity
{
私有只读函数上下文提供程序;
公共EntityFrameworkRepository(Func contextProvider)
{
_contextProvider=contextProvider;
}
公共IQueryable查询()
{
var context=_contextProvider();
返回context.Set().AsNoTracking();
}
//为简洁起见,删除了一些方法
}
添加第二个(默认)构造函数。这样,EF迁移可以在从命令行运行时使用此构造函数,而您可以让应用程序使用第二个构造函数

当您添加第二个构造函数时,
DbContext
上的Simple Injector的自动连接功能会丢失,但这应该不是问题;您只需按如下方式连接上下文:

IUserContext userContext = new AspNetUserContext();

container.RegisterSingleton<IUserContext>(userContext);

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

container.RegisterSingleton<Func<DbContext>>(contextProducer.GetInstance);
IUserContext userContext=new AspNetUserContext();
RegisterSingleton(userContext);
var contextProducer=lifety.Scoped.CreateProducer(
()=>新的MyDbContext(userContext),
容器);
RegisterSingleton(contextProducer.GetInstance);

这个答案只是为了向更多的用户显示我最终得到了什么@史蒂文的答案是正确的

为了能够在支持迁移的同时将依赖项注入到
DbContext
,我们必须使用两个构造函数。一个用于迁移,一个用于应用程序

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);
    }
}
公共类MyDbContext:DbContext
{
私有只读IUserContext\u userContext;
//用于迁移
public MyDbContext():base(“DefaultConnectionString”)
{
SetInitializer(新的MigrateDatabaseToLatestVersion());
}
//申请
公共MyDbContext(IUserContext userContext):基(“DefaultConnectionString”)
{
_userContext=userContext;
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
//…为简洁起见,删除了代码
基于模型创建(modelBuilder);
}
}
然后将其连接到合成根中,如:

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();
publicstaticvoidregisterentityframework(此容器,Func上下文),其中TContext:DbContext
{
如果(container==null)抛出新的ArgumentNullException(nameof(container));
var contextProducer=lifety.Scoped.CreateProducer(上下文,容器);
container.RegisterSingleton(()=>contextProducer.GetInstance);
}
var userContext=new AspNetHttpUserContext();
var container=新容器();
container.Options.DefaultScopedLifestyle=新的WebApiRequestLifestyle();
RegisterSingleton(userContext);
container.RegisterEntityFramework(()=>newwayfinderdbcontext(userContext));
container.Verify();

谁在向您发送此消息?EF migrations?是的,错误来自EntityFramework.dll中发生的“System.Data.Entity.migrations.Infrastructure.MigrationException”,但在返回状态集期间未在用户代码中处理:
var context=\u contextProvider();返回context.Set().AsNoTracking()
@janhartmann什么是
实体
(约束)?我认为您也应该指定一个
约束,因为现在
tenty
可能是一个不允许的接口实体只是一个在我的应用程序中定义实体的抽象类,但是是的-也许它应该更具体一些。:-)发布完整的异常,即
exception.ToString()
的结果。这包括任何内部异常和引发异常的方法的调用堆栈。当前的错误消息只是说“发生了一些其他问题,请查看内部以了解详细信息”。感谢Steven,我们将尝试此功能。我必须将compliation的单例注册更改为:
container.RegisterSingleton(()=>contextProducer.GetInstance)工作得很有魅力!我会将你的答案标记为解决方案,并将完整解决方案的答案发布给其他人(或者这是不允许的?)。@janhartmann:我认为发布完整解决方案是一个很好的实践。更多的人应该这样做。
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();