使用Generic Repository和IoC在运行时更改DB



我正在开发一个N层web应用程序,该应用程序使用Simple Injector for IoC和通用存储库模式为每个客户端使用一个数据库。

当我试图更改数据库连接时,我发现注入器在Glopal.asax中的Application_Start中运行,我仍然无法确定谁是设置连接字符串的当前用户,我需要在运行时而不是在Application _Start中更改它,有什么想法吗?


我的存储库类示例

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;
        }
    }
}

DBContxt类示例

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);
    }
}

首先,你会遇到一个严重的问题,因为要做到这一点,你必须知道每个用户的密码,或者以某种方式让它变得清楚。我不确定是否能做到。如果可以的话,下面的代码将为您提供一个良好的起点。

如果你仍然想这样做,你需要一个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);
});

注意:对于实体框架,您可能需要调整ConnectionFactory。

一个更好的解决方案是使用与SQL server集成的安全性,并让您的AppPool模拟当前用户。

更好的方法是使用单个用户帐户(AppPool标识)连接到数据库,并通过在模型中添加一些额外的表来控制应用程序中的身份验证,并在IRepository接口上添加类似IAuthorizedRepository的内容。看看围绕这个主题的讨论:如何实现基于行的安全

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

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

一旦您弄清楚了这一点,一种方法就是重写web.config中的连接字符串http://www.beansoftware.com/ASP.NET-Tutorials/Modify-Web.Config-Run-Time.aspx我不太喜欢这个主意。

更好的方法是在创建DB上下文时传递连接字符串:首先将连接字符串传递给代码DbContext

相关内容

最新更新