C# 使用通用存储库和IoC在运行时更改数据库

C# 使用通用存储库和IoC在运行时更改数据库,c#,repository-pattern,simple-injector,C#,Repository Pattern,Simple Injector,我正在开发一个N层web应用程序,它为每个客户机使用一个数据库,带有简单的IoC注入器和通用存储库模式 当我尝试更改db连接时,我发现注入器在Glopal中的应用程序启动中运行。asax中,我仍然无法确定当前用户是谁来设置连接字符串,我需要在运行时而不是应用程序启动中进行更改,有什么想法吗 我的存储库类的示例 public class Repository<T> : IRepository<T> where T : BaseEntity { private re

我正在开发一个N层web应用程序,它为每个客户机使用一个数据库,带有简单的IoC注入器和通用存储库模式

当我尝试更改db连接时,我发现注入器在Glopal中的应用程序启动中运行。asax中,我仍然无法确定当前用户是谁来设置连接字符串,我需要在运行时而不是应用程序启动中进行更改,有什么想法吗


我的存储库类的示例

public class Repository<T> : IRepository<T> where T : BaseEntity
{
    private readonly IDBContext _context;
    private IDbSet<T> _entities;

    public Repository(IDBContext context)
    {
        _context = context;
    }

    private IDbSet<T> Entities
    {
        get
        {
            if (_entities == null)
            {
                _entities = _context.Set<T>();
            }
            return _entities;
        }
    }
}
公共类存储库:IRepository其中T:BaseEntity
{
私有只读IDBContext _context;
私有IDbSet_实体;
公共存储库(IDBContext上下文)
{
_上下文=上下文;
}
私有IDbSet实体
{
得到
{
如果(_entities==null)
{
_实体=_context.Set();
}
返回实体;
}
}
}

DbContext类的示例

public class DBContext: DbContext, IDBContext
{
    public DBContext()
        : base("Name=DbConnectionString")
    {
        Database.SetInitializer<DBContext>(null);
    }

    //public DBContext(ConnectionStr connection)
    //    : base(connection.conn)
    //{
    //    Database.SetInitializer<DBContext>(null);
    //}

    public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
    {
        return base.Set<TEntity>();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
            .Where(type => !String.IsNullOrEmpty(type.Namespace))
            .Where(
                type =>
                    type.BaseType != null && type.BaseType.IsGenericType &&
                    type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
        foreach (var type in typesToRegister)
        {
            dynamic configurationInstance = Activator.CreateInstance(type);
            modelBuilder.Configurations.Add(configurationInstance);
        }
        base.OnModelCreating(modelBuilder);
    }
}
公共类DBContext:DBContext,IDBContext { 公共DBContext() :base(“Name=DbConnectionString”) { Database.SetInitializer(null); } //公共DBContext(ConnectionStr连接) //:底座(连接.连接) //{ //Database.SetInitializer(null); //} public new IDbSet Set(),其中tenty:BaseEntity { 返回base.Set(); } 模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder) { var typesToRegister=Assembly.getExecutionGassembly().GetTypes() .Where(type=>!String.IsNullOrEmpty(type.Namespace)) .在哪里( 类型=> type.BaseType!=null&&type.BaseType.IsGenericType&& type.BaseType.GetGenericTypeDefinition()==typeof(EntityTypeConfiguration)); foreach(TypeStoreRegister中的变量类型) { dynamic configurationInstance=Activator.CreateInstance(类型); modelBuilder.Configurations.Add(configurationInstance); } 基于模型创建(modelBuilder); } }
您将遇到一个问题-您如何知道用户对他们选择的任何数据库都有效

他们是否从下拉列表中选择数据库,然后输入凭据?(可能是个坏主意)您是否有具有用户名//密码//数据库映射的主数据库

一旦你明白了这一点,一种方法就是重写web.config中的连接字符串。我不太喜欢这个想法


更好的方法是在创建数据库上下文时传递连接字符串:

您将遇到一个问题-您如何知道用户对他们选择的任何数据库都有效

他们是否从下拉列表中选择数据库,然后输入凭据?(可能是个坏主意)您是否有具有用户名//密码//数据库映射的主数据库

一旦你明白了这一点,一种方法就是重写web.config中的连接字符串。我不太喜欢这个想法


更好的方法是在创建DB上下文时传递连接字符串:

首先,这里会有一个严重的问题,因为要这样做,您必须知道每个用户的密码,或者以某种方式使其简单明了。我不确定是否能做到。如果可以,下面的代码将为您提供一个良好的起点

如果您仍然想这样做,您需要一个UserContext服务,它从HttpContext返回当前用户。并由此构建您的连接字符串

这可能或应该是这样的:

public interface IUserContext
{
    string CurrentUser { get; } 
}

public class HttpUserContext : IUserContext
{
    public string CurrentUser
    {
        get { return HttpContext.Current.User.Identity.Name; }
    }
}

public class ConnectionStringFactory
{
    private readonly IUserContext userContext;
    private readonly string partialConnectionString;

    public ConnectionStringFactory(
        IUserContext userContext, 
        string partialConnectionString)
    {
        this.userContext = userContext;
        this.partialConnectionString = partialConnectionString;
    }

    public string GetConnectionString()
    {
            var builder = new SqlConnectionStringBuilder(partialConnectionString);
            builder.UserID = this.userContext.CurrentUser;
            return builder.ConnectionString;
    }
}

//register like this
var connectionStringFactory = new ConnectionStringFactory(
         new HttpUserContext(), 
         "yourconnectionString");
container.RegisterSingle(connectionStringFactory);

container.RegisterPerWebRequest<YourDbContext>(() =>
{
    var connectionFactory = container.GetInstance<ConnectionStringFactory>();
    var entityConnectionString = connectionFactory.GetConnectionString;
    return new YourDbContext(entityConnectionString);
});
公共接口IUserContext
{
字符串CurrentUser{get;}
}
公共类HttpUserContext:IUserContext
{
公共字符串当前用户
{
获取{return HttpContext.Current.User.Identity.Name;}
}
}
公共类连接字符串工厂
{
私有只读IUserContext userContext;
私有只读字符串partialConnectionString;
公共连接字符串工厂(
IUserContext用户上下文,
字符串部分连接字符串)
{
this.userContext=userContext;
this.partialConnectionString=partialConnectionString;
}
公共字符串GetConnectionString()
{
var builder=new-SqlConnectionStringBuilder(partialConnectionString);
builder.UserID=this.userContext.CurrentUser;
返回builder.ConnectionString;
}
}
//像这样登记
var connectionStringFactory=新的connectionStringFactory(
新建HttpUserContext(),
“您的连接字符串”);
容器注册表单(连接字符串工厂);
container.RegisterWebRequest(()=>
{
var connectionFactory=container.GetInstance();
var entityConnectionString=connectionFactory.GetConnectionString;
返回新的YourDbContext(entityConnectionString);
});
注意:对于实体框架,您可能需要调整ConnectionFactory

更好的解决方案是将集成安全性与SQL server结合使用,并让应用程序池模拟当前用户


更好的方法是使用单个用户帐户(AppPool标识)连接到数据库,并通过向模型中添加一些额外的表来控制应用程序中的身份验证,并在
IRepository
界面上添加类似于
IAuthorizedRepository
的内容。请看一下关于这个主题的讨论:

首先,您在这里会遇到一个严重的问题,因为要做到这一点,您必须知道每个用户的密码,或者以某种方式使其简单明了。我不确定是否能做到。如果可以,下面的代码将为您提供一个良好的起点

如果您仍然想这样做,您需要一个UserContext服务,它从HttpContext返回当前用户。并由此构建您的连接字符串

这可能或应该是这样的:

public interface IUserContext
{
    string CurrentUser { get; } 
}

public class HttpUserContext : IUserContext
{
    public string CurrentUser
    {
        get { return HttpContext.Current.User.Identity.Name; }
    }
}

public class ConnectionStringFactory
{
    private readonly IUserContext userContext;
    private readonly string partialConnectionString;

    public ConnectionStringFactory(
        IUserContext userContext, 
        string partialConnectionString)
    {
        this.userContext = userContext;
        this.partialConnectionString = partialConnectionString;
    }

    public string GetConnectionString()
    {
            var builder = new SqlConnectionStringBuilder(partialConnectionString);
            builder.UserID = this.userContext.CurrentUser;
            return builder.ConnectionString;
    }
}

//register like this
var connectionStringFactory = new ConnectionStringFactory(
         new HttpUserContext(), 
         "yourconnectionString");
container.RegisterSingle(connectionStringFactory);

container.RegisterPerWebRequest<YourDbContext>(() =>
{
    var connectionFactory = container.GetInstance<ConnectionStringFactory>();
    var entityConnectionString = connectionFactory.GetConnectionString;
    return new YourDbContext(entityConnectionString);
});
公共互联网