C# MVP依赖注入

C# MVP依赖注入,c#,winforms,mvp,C#,Winforms,Mvp,使用MVP,构建和依赖注入的正常顺序是什么 通常,您会为每个视图创建一个演示者,并将视图传递给构造函数上的演示者。但如果你有: 多个视图需要侦听其上事件的服务 多个视图都指向同一数据模型缓存 是否有人可以显示从用户单击到服务器返回的服务数据的正常信息流。以下是我的工作: 首先,我定义这些接口: public interface IView<TPresenter> { TPresenter Presenter { get; set; } } public interface

使用MVP,构建和依赖注入的正常顺序是什么

通常,您会为每个视图创建一个演示者,并将视图传递给构造函数上的演示者。但如果你有:

  • 多个视图需要侦听其上事件的服务
  • 多个视图都指向同一数据模型缓存
  • 是否有人可以显示从用户单击到服务器返回的服务数据的正常信息流。

    以下是我的工作:

    首先,我定义这些接口:

    public interface IView<TPresenter>
    {
        TPresenter Presenter { get; set; }
    }
    
    public interface IPresenter<TView, TPresenter>
        where TView : IView<TPresenter>
        where TPresenter : IPresenter<TView, TPresenter>
    {
        TView View { get; set; }
    }
    
    公共接口IView
    {
    TPresenter演示程序{get;set;}
    }
    公共接口IPresenter
    TView:IView在哪里
    其中TPresenter:IPresenter
    {
    TView视图{get;set;}
    }
    
    然后这个抽象演示者类:

    public abstract class AbstractPresenter<TView, TPresenter> : IPresenter<TView, TPresenter>
        where TView : IView<TPresenter>
        where TPresenter : class, IPresenter<TView, TPresenter>
    {
        protected TView view;
    
        public TView View
        {
            get { return this.view; }
            set
            {
                this.view = value;
                this.view.Presenter = this as TPresenter;
            }
        }
    }
    
    公共抽象类AbstractPresenter:IPresenter
    TView:IView在哪里
    其中TPresenter:class,IPresenter
    {
    受保护的电视视图;
    公共电视视图
    {
    获取{返回this.view;}
    设置
    {
    这个视图=值;
    this.view.Presenter=此为TPresenter;
    }
    }
    }
    
    视图是通过属性而不是构造函数注入的,以允许在setter中产生双向影响。请注意,需要使用安全类型转换

    然后,我的具体演示者是:

    public class MyPresenter : AbstractPresenter<IMyView, MyPresenter>
    {
        //...
    }
    
    公共类MyPresenter:AbstractPresenter
    {
    //...
    }
    
    其中
    IMyView
    实现
    IView
    。具体的视图类型必须存在(例如,
    MyView
    ),但解析它的是容器:

  • 我注册了MyPresenter,在容器中输入它自己,并有一个暂时的行为
  • 我在容器中以瞬态行为将
    MyView
    注册为
    IMyView
  • 然后我向容器请求一个
    MyPresenter
  • 容器实例化MyView
  • 它实例化了一个
    MyPresenter
  • 它通过
    AbstractPresenter.view
    属性将视图注入演示者
  • setter代码完成双向关联
  • 容器返回一对演示者/视图

  • 它允许您将其他依赖项(服务、回购)注入视图和演示者。但在您描述的场景中,我建议您将服务和缓存注入演示者,而不是视图。

    在WinForms中,我更喜欢简单的方法。通常,您要处理的是设计图面上的几个用户控件——将这些控件作为您的视图类。NET为您创建控件层次结构(通过InitializeComponent)。如果使用该模式,则每个视图都会实例化它的presenter。(您可以直接或通过询问IOC容器来完成此操作。)使用构造函数注入将对视图接口的引用传递给演示者的构造函数。然后,演示者可以将自己连接起来以查看事件。对模型重复此过程:演示者实例化模型并连接到其事件。(在这种情况下,您不需要构造函数注入,因为被动视图表示演示者保留对模型的引用,反之亦然。)

    我发现这种方法的唯一缺点是正确管理模型和演示者的生命周期。您希望视图尽可能简单,因此可能不希望它保持对演示者的引用。但是,这意味着您已经将此presenter对象与绑定到视图的事件处理程序挂起。此设置可防止对视图进行垃圾收集。一种解决方案是让视图发布一个指示其关闭的事件。演示者将接收事件并删除其模型订阅和视图订阅。web中的对象现在被正确地解除引用,垃圾收集器可以继续工作

    你会得到如下结果:

    public interface IView
    {
       ...
       event Action SomeEvent;
       event EventHandler Disposed;
       ...
    }
    
    // Note that the IView.Disposed event is implemented by the 
    // UserControl.Disposed event. 
    public class View : UserControl, IView
    {
       public event Action SomeEvent;
    
       public View()
       {
          var presenter = new Presenter(this);
       }
    }
    
    public interface IModel
    {
       ...
       event Action ModelChanged;
       ...
    }
    
    public class Model : IModel
    {
       ...
       public event Action ModelChanged;
       ...
    }
    
    public class Presenter
    {
       private IView MyView;
       private IModel MyModel;
    
       public Presenter(View view)
       {
          MyView = view;
          MyView.SomeEvent += RespondToSomeEvent;
          MyView.Disposed += ViewDisposed;
    
          MyModel = new Model();
          MyModel.ModelChanged += RespondToModelChanged;
       }
    
       // You could take this a step further by implementing IDisposable on the
       // presenter and having View.Dispose() trigger Presenter.Dispose().
       private void ViewDisposed(object sender, EventArgs e)
       {
          MyView.SomeEvent -= RespondToSomeEvent;
          MyView.Disposed -= ViewDisposed;
          MyView = null;
    
          MyModel.Modelchanged -= RespondToModelChanged;
          MyModel = null;
       }
    }
    
    通过使用IOC并向IOC容器询问IModel(在Presenter类中)和IPresenter(在View类中)的实现,可以进一步解耦此示例。

    接口IEmployee
    {
    int EmployeeId{get;}
    字符串名{get;}
    字符串LastName{get;}
    }
    接口雇员储蓄
    {
    作废保存员工(IEEMPLOYEE员工);
    IEEmployee GetEmployeeById(int employeeId);
    i雇员[]雇员{get;}
    }
    界面IEmployeeView
    {
    事件操作OnEmployeeSaved;
    }
    接口IEmployeeController
    {
    IEmployeeView视图{get;}
    IEEmployeeRepository存储库{get;}
    i雇员[]雇员{get;}
    }
    分部类EmployeeView:UserControl,IEEmployeeView
    {
    公共雇员视图()
    {
    InitComponent();
    }
    }
    类EmployeeController:IEEmployeeController
    {
    私人雇员视图;
    私人雇员存储库;
    公共EmployeeController(IEEmployeeView视图,IEEmployeeRepository存储库)
    {
    this.repository=存储库;
    this.view=视图;
    this.view.OnEmployeeSaved+=新操作(view\u OnEmployeeSaved);
    }
    无效视图\u OneEmployeeSaved(IEEmployeeEmployee)
    {
    repository.SaveEmployee(employee);
    }
    公共IEmployeeView视图
    {
    得到
    { 
    返回视图;
    }
    }
    公共雇员存储库
    {
    得到
    {
    返回存储库;
    }
    }
    公共雇员[]名雇员
    {
    得到
    {
    返回存储库。员工;
    }
    }
    }
    
    WinformsMVP是一个非常好的Windows窗体MVP框架。使用此框架,您可以轻松地跨多个视图插入服务。这是一篇包含示例源代码的好文章,介绍了如何使用该框架。

    如何处理IDisposable问题?特别是,打破循环引用以允许垃圾收集?
    interface IEmployee
    {
        int EmployeeId {get;}
        string FirstName {get;}
        string LastName {get;}
    }
    interface IEmployeeRepository
    {
        void SaveEmployee(IEmployee employee);
        IEmployee GetEmployeeById(int employeeId);
        IEmployee[] Employees { get; }
    }
    interface IEmployeeView
    {
        event Action<IEmployee> OnEmployeeSaved;
    }
    
    interface IEmployeeController
    {
        IEmployeeView View {get;}
        IEmployeeRepository Repository {get;}
        IEmployee[] Employees {get;}        
    }
    
    partial class EmployeeView: UserControl, IEmployeeView
    {
        public EmployeeView()
        {
            InitComponent();
        }
    }
    class EmployeeController:IEmployeeController
    {
        private IEmployeeView view;
        private IEmployeeRepository repository;
        public EmployeeController(IEmployeeView view, IEmployeeRepository repository)
        {
            this.repository = repository;
            this.view = view;
            this.view.OnEmployeeSaved+=new Action<IEmployee>(view_OnEmployeeSaved);
        }
    
        void  view_OnEmployeeSaved(IEmployee employee)
        {
            repository.SaveEmployee(employee);
        }
        public IEmployeeView View 
        {
            get
            { 
                return view;
            }
        }
        public IEmployeeRepository Repository
        {
            get
            {
                return repository;
            }
        }
    
        public IEmployee[] Employees
        {
            get 
            {
                return repository.Employees;
            }
        }
    }