c# - 使用 Autofac 解析 IRepository<T> 的多个具体类?

标签 c# dependency-injection autofac

我是 AutoFac 的新手,我遇到了两个问题,我需要使用 MVVM 在我的 WPF 项目中实现这些问题。我正在使用一个接口(interface)来实现存储库,但我将为 SQL、XML 和 CSV 实现多个存储库。所以我的界面是这样的:

public interface IRepository<T> : IReadOnlyRepository<T>, IWriteOnlyRepository<T>
{
}

// covariance interface
public interface IReadOnlyRepository<out T> : IDisposable
{
    T FindById(int id);
    IEnumerable<T> GetAllRecords();
}

// contravariance interface
public interface IWriteOnlyRepository<in T> : IDisposable
{
    void Add(T item);
    void Delete(T item);    
    int Save(); 
}

public class SQLRepository<T> : IRepository<T>
{
    // implements the interface using Entity Framework
}

public class XMLRepository<T> : IRepository<T>
{
    // implements the interface using XML Serializer/Deserializer
}

public class CSVRepository<T> : IRepository<T>
{
    // Implements the interface for TextReader/TextWriter for CSV Files (Excel)
}

问题来了:老板告诉我,客户需要在运行程序的同时更改存储库。所以我需要在运行时动态更改存储库。默认将是 SQL Server,但客户端可能希望更改为 XML...而不丢失存储库中已有的数据。其背后的原因是,如果他们从 SQL 加载配置但他们想将其保存到 XML 文件并将其发送给他们的客户端,他们可以这样做

-- 或者 --

他们从他们的一个客户那里得到一个 XML 文件,他们想将配置保存到 SQL,他们可以这样做而不用担心重新输入数据。

我使用泛型解决了一个问题,因为我将使用相同的 POCO 数据模型类,因此它保留了数据,但随后:

  1. 如何实现 3 个不同的具体存储库类?
  2. 如何传入T的参数?

我考虑过使用“命名服务”来区分具体的存储库类和模型基类。然后我会使用 Bootstrap 看起来像这样:

public class BootStrapper
{

    public IContainer BootStrap()
    {
        var builder = new ContainerBuilder();

        builder.RegisterType<MainWindow>.AsSelf();

        builder.RegisterType<MainViewModel>.As<IMainViewModel>();

        //?? How do I resolve T of IRepository<T>?
        builder.RegisterType<SQLRepository>.Named<IRepository>("SQL") 
        builder.RegisterType<XMLRepository>.Named<IRepository>("XML")
        builder.RegisterType<CSVRepository>.Named<IRepository>("CSV")

        return builder.Build();
    }
}

public partial class App : Application
{
    protected override void OnStartUp(StartUpEventArgs e)
    {
        base.OnStartUp(e);
        var bootsrapper = new BootStrapper();
        var container = bootstrapper.BootStrap();

        // ?? How do I set the SQLRepository as default?
        var mainWindow = container.Resolve<MainWindow>(); 
        mainWindow.Show();
    }
 }

有什么建议吗?

编辑:我忘了在其中添加我在我的 ViewModel 上使用依赖注入(inject),因此,在我的 MainViewModel 中:

 public class MainViewModel
 {
      private IRepository<Model> _repository;

      public MainViewModel(IRepository<Model> repo)
      {
            _repository = _repo;
      }
 }

现在我确实按照建议尝试将代码更改为:

 builder.RegisterGeneric(typeof(SQLRepository<>).As(typeof(IRepository<>));
 builder.RegisterGeneric(typeof(XMLRepository<>).As(typeof(IRepository<>));

然后我通过单步执行来调试代码,当我点击 MainViewModel 构造函数时,它会给我 XMLRepository 类。根据我在“default registrations”的文档中阅读的内容,它将始终是 XMLRepository 而不是 SQLRepository。然后我尝试像“open generic decorator registration”一样:

 builder.RegisterGeneric(typeof(SQLRepository<>).Named("SQL", typeof(IRepository<>));
 builder.RegisterGeneric(typeof(XMLRepository<>).Named("XML", typeof(IRepository<>));


 builder.RegisterGenericDecorator(typeof(SQLRepository<>), typeof(IRepository<>), fromKey: "SQL");
 builder.RegisterGenericDecorator(typeof(XMLRepository<>), typeof(IRepository<>), fromKey: "XML");     

但是当我尝试使用 MainWindow 时如何解决它呢?

更新编辑#2

好的,所以 tdragon 问了我一个合理的问题,关于我希望如何解决这个问题。 MainWindow.xaml.cs 文件如下所示:

public partial class MainWindow : Window
{
     private MainViewModel _viewModel;

     public MainWindow(MainViewModel viewModel)
     {
          InitializeComponent();

          _viewModel = viewModel;
          DataContext = _viewModel;
     }
}

但真正的问题在于 App.xaml.cs 文件,我已经在我的原始问题中提供了代码。

最佳答案

有一篇好文章here在 autofac 文档中。

使用 RegisterGeneric() 构建器方法注册通用组件,如下所示。

  var builder = new ContainerBuilder();
  builder.RegisterGeneric(typeof(SQLRepository<>));
  builder.RegisterGeneric(typeof(XMLRepository<>));
  builder.RegisterGeneric(typeof(CSVRepository<>));
  builder.RegisterGeneric(typeof(SQLRepository<>))
               .As(typeof(IRepository<>))
               .InstancePerLifetimeScope();
  builder.RegisterGeneric(typeof(XMLRepository<>))
               .As(typeof(IRepository<>))
               .InstancePerLifetimeScope();
  builder.RegisterGeneric(typeof(CSVRepository<>))
               .As(typeof(IRepository<>))
               .InstancePerLifetimeScope();
  builder.Register(c => new Myclass()).OnActivating(
                e =>
                {
                    e.Instance.SqlTaskRepo = e.Context.Resolve<SQLRepository<Task>>();

                }
                );

已更新

您可以通过扫描程序集来解决 T,这将是更好的解决方法,请看下面的代码希望它能帮助您

    builder.RegisterGeneric(typeof(SQLRepository<>));
    builder.RegisterGeneric(typeof(XMLRepository<>));
    builder.RegisterGeneric(typeof(CSVRepository<>));
    var dataAccess = Assembly.GetExecutingAssembly();
    builder.RegisterAssemblyTypes(dataAccess)
         .Where(t => typeof(SQLRepository<>).IsAssignableFrom(t));
    builder.RegisterAssemblyTypes(dataAccess)
             .Where(t => typeof(XMLRepository<>).IsAssignableFrom(t));
    builder.RegisterAssemblyTypes(dataAccess)
             .Where(t => typeof(CSVRepository<>).IsAssignableFrom(t));
    builder.RegisterType<MainViewModel>();

关于c# - 使用 Autofac 解析 IRepository<T> 的多个具体类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41606307/

相关文章:

c# - 如何在页面加载后抓取包含使用 JavaScript 更新的数据的页面?

asp.net-mvc - Autofac 错误 - 从请求实例的范围中看不到具有匹配 'AutofacWebRequest' 的标签的范围

asp.net-web-api - 使用 Autofac 和 ASP.Net Identity 将 ApplicationUserManager 传递给 ApplicationOAuthProvider

c# - 如何知道对话框是否完全关闭?

c# - 第一次 where 条件为真后是否可以停止查询? (linq 到 sql)

c# - 如果一个类需要在方法中需要时创建另一个类的多个实例,则进行依赖注入(inject)

php - 使用 PHPUnit 和 Mockery 进行 Laravel 测试 - 设置对 Controller 测试的依赖

Angular - 通过@Input 属性进行依赖注入(inject)

asp.net - 解析 ASP.NET 后台任务中的 Autofac 组件

c# - C# 中的 SOAP 请求