任何人都可以解释创建松散耦合视图模型的理论。
我附上了一些示例代码,试图解释我的意思。
我只为此示例提供了两个示例类
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等,只要所有方法等匹配代码仍然有效。
然后我创建了一个viewmodel
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部分。
如果我不想使用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
希望上面的例子对我正在学习的东西有意义
由于
乔治
答案 0 :(得分:1)
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的event aggregator课,因为它可以很好地解决你的耦合问题。无论如何,这个库值得一看,以学习MVVM模式。
IEventAggregator
允许您将类的实例订阅到聚合器的实例。如果多个视图模型共享事件聚合器的实例,则可以通过loosley耦合方式轻松地将事件从一个发送到另一个。
我重构了你的原始代码,使其更适合实际的MVVM模式(你的第一个问题,让我们说这个实现更合适)。我添加了一个Employer
类,它基本上是主要对象。它只有一个id。 EmployerDetails
和EmployerAddress
也有一个新属性EmployerId
,它是对所属Employer
的引用。
我已将所有内容都放在MyRepository
类中查询数据!
对于这三个类中的每一个都存在三个单独的视图模型,它们仅通过它们共享的事件聚合器耦合(回答您的第二个问题)。 EmployerViewModel
管理Employer
类型的主要数据对象,并在所选Employer
更改后立即发布事件。新值将传递到EmployerChangedEvent
,然后由处理此特定类型事件(IHandle<EmployerChangedEvent
)的视图模型捕获。在Handle()
实施中,将通过的雇主放入接收视图模型的私有字段中。
这只是一个模拟用户输入的控制台应用程序,但尝试在两个句柄方法上设置断点,SelectedEmployer
更改。
我认为我在Main()
方法中所做的某些事情应该在你的控制器中完成。我必须提到这段代码只是为了展示MVVM模式的好处,在某些情况下它可能过于抽象。此外,根本没有涉及有效查询存储库的事情!
我认为我的回答也解决了你的第3个问题,因为我看不再需要foreach
。
如果您想运行此代码,请记得引用Caliburn.Micro
。只需通过NuGet获取它或下载它here。