簡體   English   中英

松耦合視圖模型理論

[英]Theory on loosely coupled viewmodel

誰能解釋一下創建松耦合視圖模型的理論。

我在下面附加了一些示例代碼,試圖解釋我的意思。

我有2個示例類,僅用於此示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.UI.Models
{
    public class EmployerAddress
    {
        public string address { get; set; }
        public string city { get; set; }
        public string region { get; set; }
        public string country { get; set; }
        public string postZipCode { get; set; }
    }

    public class EmployerDetails
    {
        public string position { get; set; }
        public string gender { get; set; }
        public string dob { get; set; }
    }

    public class DisplayEmployerAddress : IDisplayEmployerAddress
    {
        public IEnumerable<EmployerAddress> employerAddr()
        {
            List<EmployerAddress> Data = new List<EmployerAddress>();
            Data.Add(new EmployerAddress
            {
                address = "address1",
                city = "city1",
                region = "region1",
                country = "country1",
                postZipCode = "post zip1"
            });
            return Data;
        }
    }

    public class DisplayEmployerDetails : IDisplayEmployerDetails
    {
        public IEnumerable<EmployerDetails> employerDetails()
        {
            List<EmployerDetails> Data = new List<EmployerDetails>();
            Data.Add(new EmployerDetails
            {
                position = "trainee",
                gender = "male",
                dob = "22-08-1964"
            });
            Data.Add(new EmployerDetails
            {
                position = "trainee2",
                gender = "male2",
                dob = "22-08-1970"
            });
            return Data;
        }
    }
}

上面的代碼具有以下接口:

IEnumerable<EmployerAddress> employerAddr();
IEnumerable<EmployerDetails> employerDetails();

然后,我使用Ninject綁定以上內容。

kernel.Bind<IDisplayEmployerAddress>().To<DisplayEmployerAddress>().InSingletonScope();
kernel.Bind<IDisplayEmployerDetails>().To<DisplayEmployerDetails>().InSingletonScope();

至此一切正常,我可以更改DisplayEmployerAddress等,只要所有方法等匹配,代碼仍然可以使用。

然后創建一個視圖模型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.UI.Models
{
    public class EmployerDetailsViewModel
    {
        public string age { get; set; }
        public IEnumerable<EmployerAddress> EmployerAddress { get; set; }
        public IEnumerable<EmployerDetails> EmployerDetails { get; set; }
    }
}

但是現在這會引起問題,因為EmployerAddress已經緊密耦合,因此,如果我更改代碼,則現在必須在2個地方進行更新。

在我的控制器中

public class HomeController : Controller
    {
        private readonly IDisplayEmployerAddress _address;
        private readonly IDisplayEmployerDetails _details;

        public HomeController(IDisplayEmployerAddress address,
                              IDisplayEmployerDetails details)
        {
            _address = address;
            _details = details;
        }
        public ActionResult Index()
        {
            ViewBag.Title = "Title";
            var Address = _address.employerAddr();
            var Details = _details.employerDetails().AsEnumerable();
            var Age = _details.employerDetails().FirstOrDefault().dob;
            var employerModel = new EmployerDetailsViewModel
            {
                EmployerAddress = Address,
                EmployerDetails = Details,
                age = age.calAge(Age)
            };
            return View(employerModel);
        }

我將控制器保持輕巧,因為我讀過的所有書籍都說在控制器中保留盡可能少的代碼,因此要計算年齡,我使用靜態類。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.UI.Models
{
    public static class age
    {
        public static string calAge(string dob)
        {
            //Would cal age here
            return "48";

        }
    }
} 

所以我的問題是3個部分。

  1. 這個例子是正確的方法嗎?
  2. 由於我的視圖模型已經緊密耦合,如何使它松散耦合。
  3. 如果我不想使用foreach循環,該如何刪除EmployerDetails中的每一項

    該雇主擔任@ Model.EmployerDetails.position職位,性別為@ Model.EmployerDetails.gender

     <ul> @foreach (var d in Model.EmployerAddress) { <li>@d.address</li> <li>@d.city</li> <li>@d.country</li> <li>@d.region</li> <li>@d.postZipCode</li> } </ul> <ul> @foreach (var dd in Model.EmployerDetails) { <li>@dd.position</li> <li>@dd.gender</li> <li>@dd.dob</li> } </ul> 

至今

該雇主擔任@ Model.EmployerDetails.position職位,性別為@ Model.EmployerDetails.gender

解決問題3,將代碼更改為@ Model.EmployerDetails.FirstOrDefault()。position

希望上面的例子對我要學習的東西有意義

謝謝

喬治

public class Employer
{
    public int Id { get; set; }
}

public class EmployerAddress
{
    public string Address { get; set; }
    public string City { get; set; }
    public string Region { get; set; }
    public string Country { get; set; }
    public string PostZipCode { get; set; }

    public int EmployerId { get; set; }
}

public class EmployerDetails
{
    public string Position { get; set; }
    public string Gender { get; set; }
    public string Dob { get; set; }

    public int EmployerId { get; set; }
}

public class MyRepository : IMyRepository
{
    public IEnumerable<Employer> GetEmployers()
    {
        return new List<Employer>
            {
                new Employer {Id = 1},
                new Employer {Id = 2}
            };
    }

    public IEnumerable<EmployerAddress> GetEmployeeAddresses()
    {
        return new List<EmployerAddress>
            {
                new EmployerAddress
                    {
                        EmployerId = 1,
                        Address = "address1",
                        City = "city1",
                        Region = "region1",
                        Country = "country1",
                        PostZipCode = "post zip1"
                    },
                new EmployerAddress
                    {
                        EmployerId = 2,
                        Address = "address2",
                        City = "city2",
                        Region = "region2",
                        Country = "country2",
                        PostZipCode = "post zip2"
                    }
            };
    }

    public IEnumerable<EmployerDetails> GetEmployeeDetails()
    {
        return new List<EmployerDetails>
            {
                new EmployerDetails
                    {
                        EmployerId = 1,
                        Position = "trainee",
                        Gender = "male",
                        Dob = "22-08-1964"
                    },
                new EmployerDetails
                    {
                        EmployerId = 2,
                        Position = "trainee2",
                        Gender = "male2",
                        Dob = "22-08-1970"
                    }
            };
    }
}

public class EmployerChangedEvent
{
    public EmployerChangedEvent(Employer selectedEmployer)
    {
        Employer = selectedEmployer;
    }

    public Employer Employer { get; set; }
}

public class EmployerViewModel
{
    private readonly IEventAggregator _events;
    private Employer _selectedEmployer;

    // Configure Ninject properly to get those types
    public EmployerViewModel(IEventAggregator events, IMyRepository myRepository)
    {
        _events = events;
        Employers = myRepository.GetEmployers().ToList();
        EmployerAddressViewModel = new EmployerAddressViewModel(_events, myRepository);
        EmployerDetailsViewModel = new EmployerDetailsViewModel(_events, myRepository);
    }

    public List<Employer> Employers { get; set; }

    public EmployerAddressViewModel EmployerAddressViewModel { get; set; }
    public EmployerDetailsViewModel EmployerDetailsViewModel { get; set; }

    public Employer SelectedEmployer
    {
        get { return _selectedEmployer; }
        set
        {
            _selectedEmployer = value;
            // this notifies the dependent view models in a loosley coupled way
            _events.Publish(new EmployerChangedEvent(_selectedEmployer));
        }
    }
}

public class EmployerAddressViewModel :
    IHandle<EmployerChangedEvent> // specifies which events shall be caught
{
    private readonly IMyRepository _myRepository;
    private Employer _selectedEmployer;

    public EmployerAddressViewModel(IEventAggregator events, IMyRepository myRepository)
    {
        _myRepository = myRepository;
        // this subscribes this view model to the passed event aggregator
        // from your main view model (EmployerViewModel)
        events.Subscribe(this);
    }

    public EmployerAddress EmployerAddress { get; set; }

    public void Handle(EmployerChangedEvent message)
    {
        _selectedEmployer = message.Employer;
        EmployerAddress = _myRepository.GetEmployeeAddresses()
                                       .FirstOrDefault(e => e.EmployerId == _selectedEmployer.Id);
    }
}

public class EmployerDetailsViewModel :
    IHandle<EmployerChangedEvent> // specifies which events shall be caught
{
    private readonly IMyRepository _myRepository;
    private Employer _selectedEmployer;

    public EmployerDetailsViewModel(IEventAggregator events, IMyRepository myRepository)
    {
        _myRepository = myRepository;
        // this subscribes this view model to the passed event aggregator
        // from your main view model (EmployerViewModel)
        events.Subscribe(this);
    }

    public EmployerDetails EmployerDetails { get; set; }

    public void Handle(EmployerChangedEvent message)
    {
        _selectedEmployer = message.Employer;
        EmployerDetails = _myRepository.GetEmployeeDetails()
                                       .FirstOrDefault(e => e.EmployerId == _selectedEmployer.Id);
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        // do this with Ninject
        var employerViewModel = new EmployerViewModel(new EventAggregator(), new MyRepository());

        // this selection should actually be user input
        employerViewModel.SelectedEmployer = employerViewModel.Employers.First();

        // select another one
        employerViewModel.SelectedEmployer = employerViewModel.Employers.Last();
    }
}

由於我不熟悉ASP.NET,因此我的回答並不表示任何UI通知。

我在這里建議使用Caliburn.Micro的事件聚合器類,因為它很好地解決了耦合問題。 無論如何,該庫對於學習MVVM模式都值得一看。

IEventAggregator允許您使用類的實例訂閱聚合器的實例。 如果多個視圖模型共享事件聚合器的一個實例,則可以輕松地以松散耦合的方式將事件從一個事件發送到另一個事件。

我重構了您的原始代碼,以使其更適合實際的MVVM模式( 您的第一個問題 ,假設這種實現更合適)。 我添加了一個Employer類,它基本上是主要對象。 它只有一個ID。 EmployerDetailsEmployerAddress還具有一個新屬性EmployerId ,該屬性是對它們所屬的Employer的引用。

我把所有東西都放在MyRepository類中查詢數據了!

對於這三個類中的每一個,都存在三個單獨的視圖模型,它們僅通過它們共享的事件聚合器耦合( 回答您的第二個問題 )。 所述EmployerViewModel管理類型的主數據對象Employer並且一旦發布一個事件作為所選擇的Employer的變化。 新值傳遞到EmployerChangedEvent ,然后由處理此類事件的視圖模型捕獲( IHandle<EmployerChangedEvent )。 在其Handle()實現中,將通過的雇主置於接收視圖模型的私有字段中。

這只是一個模擬用戶輸入的控制台應用程序,盡管嘗試在SelectedEmployer更改時將斷點放在兩個handle方法上。

我認為我在Main()方法中所做的某些事情應該在您的控制器中完成。 我不得不提到,此代碼僅用於顯示MVVM模式的好處,在某些情況下可能過度抽象。 此外,根本不涉及有效查詢存儲庫之類的事情!

我認為我的回答也解決了您的第三個問題,因為我認為不再需要foreach了。

如果要運行此代碼,請記住引用Caliburn.Micro 只需通過NuGet獲取或在此處下載即可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM