【问题标题】:is there another way of changing Database Instance in Autofac是否有另一种在 Autofac 中更改数据库实例的方法
【发布时间】:2014-08-02 23:48:55
【问题描述】:

我有一个使用多个数据库的应用程序。

我发现我可以通过使用连接构建器来改变它。像这样:

var configNameEf = "ProjectConnection";
var cs = System.Configuration.ConfigurationManager.ConnectionStrings[configNameEf].ConnectionString;
var sqlcnxstringbuilder = new SqlConnectionStringBuilder(cs);
sqlcnxstringbuilder.InitialCatalog = _Database;

但是我需要更改 UnitOfWork 的 autofac Lifescope,以便它现在将请求重定向到好的数据库实例。

一段时间后我发现我可以通过 DelegatedHandler 这样做:

HttpConfiguration config = GlobalConfiguration.Configuration;
DependencyConfig.Register(config, sqlcnxstringbuilder.ToString());
request.Properties["MS_DependencyScope"] = config.DependencyResolver.GetRequestLifetimeScope();

问题是,有没有其他方法可以改变请求的 MS_DependencyScope 参数。这个解决方案有效,但我认为它有点阴暗。

这里是 DependencyConfig 中的注册表:

public static void Register(HttpConfiguration config, String bdContext = null)
        {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        builder.Register(_ => new ProjectContext(bdContext)).As<ProjectContext>().InstancePerApiRequest();

        builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerApiRequest();

        // Register IMappingEngine
        builder.Register(_ => Mapper.Engine).As<IMappingEngine>().SingleInstance();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(builder.Build());
        config.DependencyResolver.BeginScope();

    }

【问题讨论】:

  • 这个问题并不完全清楚 - 您是否尝试根据每个请求更改数据库?或者您是否根据某种部署配置在应用程序范围内更改它?
  • 如果它与上次调用不同,它会根据每个请求进行更改。

标签: c# entity-framework-4 autofac


【解决方案1】:

问题的描述方式和我评论的答案听起来的方式,你有以下情况:

  • 该应用程序使用每个请求的生命周期工作单元。我从您的注册中看到了这一点。
  • 在给定的时间点,应用程序中只使用一个数据库。也就是说,每个请求不必确定不同的数据库;他们都使用相同的,直到连接字符串发生变化。从使用固定应用程序设置检索数据库的方式可以看出这一点。
  • 配置中的连接字符串可能会改变,此时使用的数据库需要改变。

假设我已经正确理解了这个问题......

如果应用程序设置在 web.config 中(如它所显示的那样),那么更改 web.config 中的字符串实际上会重新启动应用程序。 这个问题更详细地讨论了这一点: How to prevent an ASP.NET application restarting when the web.config is modified?

如果是这种情况,您无需做任何工作 - 只需将数据库注册为单例,当 web.config 更改时,应用重新启动,重新运行应用启动逻辑,获取新数据库,奇迹发生了。

如果应用设置不在 web.config 中,那么您可能应该创建一个项目上下文工厂类。

工厂将充当读取配置和建立与数据库的连接的逻辑的封装。在设置未更改时,它还将用作缓存连接的地方。

界面看起来像这样:

public interface IProjectContextFactory
{
  ProjectContext GetContext();
}

一个简单的实现(没有锁定、错误处理、日志记录以及您应该放入的所有好东西)可能是:

public class ProjectContextFactory : IProjectContextFactory
{
  private ProjectContext _currentContext = null;
  private string _currentConnectionString = null;
  private const string ConnectionKey = "ProjectConnection";

  public ProjectContext GetContext()
  {
    // Seriously, don't forget the locking, etc. in here
    // to make this thread-safe! I'm omitting it for simplicity.
    var cs = ConfigurationManager.ConnectionStrings[ConnectionKey].ConnectionString;
    if(this._currentConnectionString != cs)
    {
      this._currentConnectionString = cs;
      var builder = new SqlConnectionStringBuilder(cs);
      builder.InitialCatalog = _Database;
      this._currentContext = new ProjectContext(builder.ToString());
    }
    return this._currentContext;
  }
}

好的,现在您有了一个工厂,它缓存了已构建的项目上下文,并且仅在配置更改时才对其进行更改。 (如果您没有缓存 ProjectContext 而是缓存数据库连接字符串或其他内容,则原则仍然适用 - 您需要一个管理缓存和检查配置的类,以便可以根据需要进行更改.)

现在您有了缓存/工厂,您可以在 Autofac 注册中使用它而不是原始连接字符串。

builder.RegisterType<ProjectContextFactory>()
       .As<IProjectContextFactory>()
       .SingleInstance();
builder.Register(c => c.Resolve<IProjectContextFactory>().GetContext())
       .As<ProjectContext>()
       .InstancePerRequest();

现在,当配置的连接字符串更改时,ProjectContext 将根据每个请求进行更改。

除此之外:我看到请求生命周期范围内发生了奇怪的事情。我在您的注册中看到您正在创建自己的请求生命周期范围。使用这种方法,您不必这样做。但是,如果您发现您仍然需要(或想要),您需要确保最初创建的生命周期范围和您创建的范围都已释放。生命周期范围不会自动获取处置并确实挂在对象引用上,以便他们可以处理处置。如果您没有正确处理此问题,则很有可能会出现细微的内存泄漏。 Autofac Web API 集成将为您处理请求生命周期的创建和处置,但如果您更改请求生命周期,就会发生奇怪的事情。

【讨论】:

  • 感谢您的回答,这正是我想要的,工厂方法似乎更适合应用程序的账单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
相关资源
最近更新 更多