使用通用存储库和 IoC 在运行时更改数据库
Change DB in the runtime using Generic Repository and IoC
我正在开发一个 N 层 Web 应用程序,它为每个客户端使用一个数据库,带有用于 IoC 的简单注入器和通用存储库模式。
当我尝试更改数据库连接时,我发现注入器在 Glopal.asax 中的 Application_Start 中运行,我仍然无法弄清楚是谁是当前用户设置连接字符串,我需要在运行时更改它而不是在Application_Start,有什么想法吗?
我的存储库示例 Class
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 示例 Class
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);
}
}
您将 运行 遇到一个问题 - 您如何知道用户对他们选择的任何数据库都是有效的?
他们是否 select 从下拉列表中输入数据库,然后输入凭据? (可能是个坏主意)你有一个主数据库有用户名 // 密码 // 数据库映射吗?
一旦你弄明白了,一种方法是重写 web.config http://www.beansoftware.com/ASP.NET-Tutorials/Modify-Web.Config-Run-Time.aspx 中的连接字符串。我不太喜欢这个主意。
更好的方法是在创建数据库上下文时传递连接字符串:Pass connection string to code-first DbContext
首先,你会遇到一个严重的问题,因为要做到这一点,你必须知道每个用户的密码,或者以某种方式使其明了。我不确定是否可以完成。如果可以的话,下面的代码会给你一个很好的起点。
如果您仍想这样做,则需要一个 UserContext 服务,该服务 returns 来自 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);
});
注意:对于 Entity Framework,您可能需要调整 ConnectionFactory。
更好的解决方案是使用 SQL 服务器的集成安全性,让您的 AppPool 模拟当前用户。
更好的方法是使用单个用户帐户(AppPool 身份)连接到您的数据库,并通过向模型添加一些额外的表并添加类似 IAuthorizedRepository
的内容来控制应用程序中的身份验证在您的 IRepository
界面之上。看看围绕这个主题的讨论:How to implement row based security
我正在开发一个 N 层 Web 应用程序,它为每个客户端使用一个数据库,带有用于 IoC 的简单注入器和通用存储库模式。
当我尝试更改数据库连接时,我发现注入器在 Glopal.asax 中的 Application_Start 中运行,我仍然无法弄清楚是谁是当前用户设置连接字符串,我需要在运行时更改它而不是在Application_Start,有什么想法吗?
我的存储库示例 Class
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 示例 Class
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);
}
}
您将 运行 遇到一个问题 - 您如何知道用户对他们选择的任何数据库都是有效的?
他们是否 select 从下拉列表中输入数据库,然后输入凭据? (可能是个坏主意)你有一个主数据库有用户名 // 密码 // 数据库映射吗?
一旦你弄明白了,一种方法是重写 web.config http://www.beansoftware.com/ASP.NET-Tutorials/Modify-Web.Config-Run-Time.aspx 中的连接字符串。我不太喜欢这个主意。
更好的方法是在创建数据库上下文时传递连接字符串:Pass connection string to code-first DbContext
首先,你会遇到一个严重的问题,因为要做到这一点,你必须知道每个用户的密码,或者以某种方式使其明了。我不确定是否可以完成。如果可以的话,下面的代码会给你一个很好的起点。
如果您仍想这样做,则需要一个 UserContext 服务,该服务 returns 来自 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);
});
注意:对于 Entity Framework,您可能需要调整 ConnectionFactory。
更好的解决方案是使用 SQL 服务器的集成安全性,让您的 AppPool 模拟当前用户。
更好的方法是使用单个用户帐户(AppPool 身份)连接到您的数据库,并通过向模型添加一些额外的表并添加类似 IAuthorizedRepository
的内容来控制应用程序中的身份验证在您的 IRepository
界面之上。看看围绕这个主题的讨论:How to implement row based security