我正在开发一个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