如何在WPF中使用DI来获得新的距离而无需询问容器

问题描述

我正在尝试使用Simpleinjector作为IOC容器从头开始开发WPF应用程序。 我是这个主题的新手,我有一些关于对象寿命和正确使用它们的问题。

我通过按照simpleinjector手册上的WPF集成指南启动了该应用程序。 但是我不明白每次服务需要时如何接收新实例

As i ask in my previous post每当服务需要时,我都需要接收一个新的unitOfWork。

就像@Steven在我以前的帖子中所说的

请注意,transient的意思是“从容器请求新实例时,始终解析该新实例”。如果您不再次请求它,那么您将在同一实例上进行操作,这可能解释了ObjectdisposedException。

the other post中,我找到了一个solutin,但我认为它有点过于复杂了,它是创建一个工厂并注入它而不是实例,因为我只想在启动方法调用container.getInstance而不是通过将容器作为依赖项传递服务

这是我必须实现的唯一方法,或者我不了解如何以DI方式进行开发?

代码示例:

public class HeaderviewmodelFactory : IWpfRaddispenserviewmodelFactory<Headerviewmodel>
{
    private readonly ProductionService _service;

    public HeaderviewmodelFactory(ProductionService service)
    {
        _service = service;
    }

    public Headerviewmodel Createviewmodel()
    {
        return new Headerviewmodel(_service);
    }
}

public class Headerviewmodel : viewmodelBase
{
    private readonly ProductionService _service;
    
    public Headerviewmodel(ProductionService service)
    {
        _service = service;
        CreateData();
    }

    private void CreateData()
    {
        _service.CreateTestCycle();
    }
}


public class CycleService : GenericDataService<Cycle>
{
    private readonly IUnitOfWork<WpfRaddispenserDbContext> _uowContext;
    
    public CycleService(IUnitOfWork<WpfRaddispenserDbContext> uowContext)
        : base(uowContext)
    {
        _uowContext = uowContext;
    }

    public void CreateTestCycle()
    {
        var cycleDataService = new GenericDataService<Cycle>(_uowContext);
        var vialDataService = new GenericDataService<Vial>(_uowContext);

        Cycle c = new Cycle();
        c.BatchName = "test";

        Vial v = new Vial();
        v.Name = "Test Vial";
        c.Vials.Add(v);

        _uowContext.CreateTransaction(IsolationLevel.ReadCommitted);
        try
        {
            vialDataService.Create(v);
            _uowContext.Persist();

            var list = vialDataService.GetAll();

            cycleDataService.Create(c);
            _uowContext.Persist();
            _uowContext.Commit();
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            _uowContext.RollBack();
            throw;
        }
        finally
        {
            _uowContext.dispose();
        }
    }
}

private static Container Bootstrap()
{
    // Create the container as usual.
    var container = new Container();
    // Register your types:
    // Register your windows and view models:

    container.Register<WpfRaddispenserDbContextFactory>(Lifestyle.Transient);
    container.Register<IUnitOfWork<WpfRaddispenserDbContext>,WpfRaddispenserUOW>();
    container.Register(typeof(CycleService));

    container.Register<IWpfRaddispenserviewmodelFactory<Productionviewmodel>,ProductionviewmodelFactory>(Lifestyle.Transient);
    container.Register<IWpfRaddispenserviewmodelFactory<Anagraphicviewmodel>,AnagraphicsviewmodelFactory>(Lifestyle.Transient);

    container.Register<IWpfRaddispenserviewmodelFactory<Headerviewmodel>,HeaderviewmodelFactory>(Lifestyle.Transient);

    container.Register<IviewmodelAbstractFactory,viewmodelAbstractFactory>(Lifestyle.Transient);

    container.Register<INavigator,Navigator>(Lifestyle.Transient);
    container.Register<MainWindowviewmodel>();
    container.Register<MainWindow>();

    //container.Options.EnableAutoVerification = false;
    //container.Verify();
    return container;
}

通过这种方式,每次创建新的视图模型时,我都会收到相同的服务,并且显然由于处理而不再提供dbcontext。

这不是相关代码,只是我为了解DI的工作原理而编写的一个示例。

解决方法

使用抽象工厂模式是最常见和推荐的方法。出于非常充分的理由,直接在应用程序中使用容器被认为是一种反模式,例如服务定位器(Service Locator is an Anti-Pattern)。

抽象工厂允许实例化对象,而无需与知道如何创建特定实例的实际实现紧密耦合。

大多数IoC框架本身都支持这种模式。大多数时候,它们为工厂提供通用接口。您在容器中注册实例(产品),框架将为您导出一个现成的工厂。您将对此框架接口的依赖项添加到对象中,例如构造函数。然后,您注册通用工厂接口。该框架将自动创建工厂实例,并将其注入相关实例,例如通过构造函数。

我对Simple Injector不太熟悉,但是该框架确实使事情变得简单。没有这样的代码生成。
但是模式非常简单(这就是为什么它如此容易实现自动化)并且绝不复杂。

示例

动态创建TInstance类型的实例所需的接口:

interface IFactory<TInstance>
{
  TInstance Create();
}

该工厂的实现:

class SaveItemFactory : IFactory<ISaveItem>
{
  ISaveItem Create() => new SaveItem();
}

需要动态创建依赖项的类型:

interface IItemManager {}

class ItemManager : IItemManager 
{
  IFactory<ISaveItem> SaveItemFactory { get; }

  public ItemManager(IFactory<ISaveItem> itemFactory) => this.SaveItemFactory = itemFactory;

  public void SaveData(object data)
  {
    ISaveItem saveItem = this.SaveItemFactory.Create();
    saveItem.SetData(data);
  }
}

配置容器:

public void Run()
{
  var container = new SimpleInjector.Container();

  container.Register<IFactory<ISaveItem>,SaveItemFactory>(Lifestyle.Singleton);
  container.Register<IItemManager,ItemManager>(Lifestyle.Singleton);

  IItemManager itemManager = container.GetInstance<IItemManager>();
  itemManager.SaveData("Some Data");
}