Injecting/Managing 在运行时使用 Entity Framework、依赖注入、工作单元和存储库模式更改连接字符串

Injecting/Managing at runtime changing connectionstrings using Entity Framework, Dependency Injection, Unit of Work and Repository Patterns

情况

我正在使用标题中提到的技术构建 Web 应用程序。该应用程序类似于用于多个客户端的 CMS 系统。客户必须使用他的公司名称和登录凭据登录到该系统。

使用提供的公司名称,我连接到存储所有客户数据库信息的数据库(静态 DbContext,每次都使用相同的连接字符串)并搜索该客户特定的数据库(每个客户都有自己的数据库)具有完全相同的设计)登录信息。一切正常。

现在是棘手的部分。要继续登录过程,我需要使用另一个 DbContext 以某种方式注入或延迟加载存储库,连接字符串是从另一个数据库的结果构建的。

我有什么

2 DbContext 从现有数据库生成,一个是静态的,一个是动态的。

然后是通用存储库 classes/interfaces:

public interface IRepository
{
    void Submit();
}

public interface IRepository<TEntity, TContext> : IRepository
    where TEntity : class
    where TContext : DbContext
{
     //crud stuff
}

public abstract class GenericRepository<TEntity, TContext> : IRepository<TEntity, TContext>
    where TEntity : class
    where TContext : DbContext
{
    private TContext _dataContext;
    private IUnitOfWork _unitOfWork;
    private readonly IDbSet<TEntity> dbset;

    protected GenericRepository(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _unitOfWork.Register(this);
    }
}

工作单元class/interface

public interface IUnitOfWork
{
    void Register(IRepository repository);
    void Commit();
}

public class UnitOfWork : IUnitOfWork 
{
    private readonly Dictionary<string, IRepository> _repositories;
    private HttpContextBase _httpContext;
    
    public UnitOfWork(HttpContextBase httpContext)        
    {            
        _httpContext = httpContext;
    }

    public void Register(IRepository repository)
    {
        _repositories.Add(repository.GetType().Name, repository);
    }        

    public void Commit()
    {
        _repositories.ToList().ForEach(x => x.Value.Submit());
    }
}

然后是 context/entity 特定的存储库

public class EmployeeRepository : GenericRepository<tbl_Medewerker, CustomerDbEntities>, IEmployeeRepository
{
    public EmployeeRepository(IUnitOfWork unitOfWork)
        : base(unitOfWork)
    {
    }
}

public interface IEmployeeRepository : IRepository<tbl_Medewerker, CustomerDbEntities>
{
}

然后是实现存储库的服务

public interface IEmployeeLoginService
{
    tbl_Medewerker GetEmployeeByLogin(string username, string password);
    tbl_Medewerker GetEmployeeByID(Guid id);
}

public class EmployeeLoginService : IEmployeeLoginService
{
    private readonly IEmployeeRepository _employeeRepository;

    public EmployeeLoginService(IEmployeeRepository employeeRepository)
    {
        _employeeRepository = employeeRepository;
    }

    public tbl_Medewerker GetEmployeeByLogin(string username, string password)
    {
        return _employeeRepository.Get(e => e.MedewerkerNaam.ToLower() == username.ToLower() && e.Password == password);
    }

    public tbl_Medewerker GetEmployeeByID(Guid id)
    {
        return _employeeRepository.GetById(id);
    }
}

最后是实现该服务并在登录操作中使用它的控制器

public class AccountController : BaseController
{
    IConnectionService _connectionService;
    IEmployeeLoginService _employeeService;

    public AccountController(IConnectionService connectionService, IEmployeeLoginService employeeService)
    {
        _connectionService = connectionService;
        _employeeService = employeeService;
    }

    [AllowAnonymous, HttpPost]
    public ActionResult Login(LoginModel login)
    {
        if ((Settings)Session["Settings"] == null)
        {
            Settings settings = new Settings();

            settings.company = _connectionService.GetCompanyName(login.CompanyName);
            if (settings.company != null)
            {
                settings.licence = _connectionService.GetLicenceByCompanyID(settings.company.Company_id);
                if (settings.licence != null)
                {
                    settings.connectionStringOrName = string.Format(@"Data Source={0};Initial Catalog={1};User ID={2};Password={3};Application Name=EntityFrameworkMUE", settings.licence.WS_DatabaseServer, settings.licence.WS_DatabaseName, settings.licence.WS_DatabaseUID, settings.licence.WS_DatabasePWD);                        
                    Session["Settings"] = settings;
                   
                    settings.user = _employeeService.GetEmployeeByLogin(login.UserName, login.Password);
                    if (settings.user != null)
                    {
                        FormsAuthentication.SetAuthCookie(string.Format("{0},{1}", settings.company.Company_id.ToString(), settings.user.Medewerker_ID.ToString()) , login.RememberMe);
                        return RedirectToAction("index", "home");                            
                    }
                }
            }
        }
        else
        {
            return RedirectToAction("index", "home");
        }

        return View();
    }
}

当然还有 autofac 引导程序:

private static void SetAutoFacContainer()
{
    var builder = new ContainerBuilder();
    builder.RegisterControllers(Assembly.GetExecutingAssembly());
    builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerHttpRequest();           
    builder.RegisterAssemblyTypes(typeof(UserRepository).Assembly)
            .Where(t => t.Name.EndsWith("Repository"))
            .AsImplementedInterfaces().InstancePerHttpRequest();
    builder.RegisterAssemblyTypes(typeof(ConnectionService).Assembly)
            .Where(t => t.Name.EndsWith("Service"))
            .AsImplementedInterfaces().InstancePerHttpRequest();

     builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>().InstancePerLifetimeScope();
     builder.RegisterModule(new AutofacWebTypesModule());

     builder.Register(att => new AuthorizeFilter(att.Resolve<IConnectionService>(), att.Resolve<IEmployeeLoginService>())).AsAuthorizationFilterFor<Controller>().InstancePerHttpRequest();
     builder.RegisterFilterProvider();

     IContainer container = builder.Build();
     DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}

我的想法是,在从存储信息的一个静态数据库中检索数据后,使用连接字符串设置一个 session 变量,并在工作单元中注入 session并以某种方式在那里使用它,但我无法理解它。

问题:

我是否正朝着正确的方向努力实现这一目标,甚至可能吗?如果不是,你会采取什么步骤来实现这个

我知道这是一篇很长的文章,我希望你们能帮助我,我对一起使用这些技术还很陌生。提前致谢 - 非常感谢!

你说的对,我用过

var mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));

识别策略将基于登录用户。在他们未登录时使用默认值。

public class CompanyNameIdentificationStrategy : ITenantIdentificationStrategy
    {

        public bool TryIdentifyTenant(out object tenantId)
        {
             var context = HttpContext.Current;
             if(context != null) 
             {
                var myUser = context.User as MyUserObject;
                if(myUser != null) 
                {
                    tenantId = myUser.CompanyName;
                    return true;
                }
             }
             return false; 
        }
}

然后你添加到你的自动事实设置:

 var s = c.Resolve<ITenantIdentificationStrategy>();
                    object id;
                    if (s.TryIdentifyTenant(out id) && id != null)
                    {
                        return id;
                    }
                    return "default"; 
                }).Keyed<string>("CompanyName");



builder.Register<Settings>(c =>
                {
                    var companyName = c.ResolveKeyed<string>("companyName");
                    if (companyName == "default")
                    {
                        return new DefaultSettings();
                    }
                    var settings = new Settings(); 
                    return settings;

                }).InstancePerLifetimeScope();

您可以解析这些代码块中的内容。我可能会设置一个键控默认设置,然后当用户登录时,设置将切换到他们的设置,应用程序的其余部分应该可以工作。