我会尽力为您提供一些指导。
假设您有一个像这样定义的现有产品 WCF 服务(我们不关心实现,目前这并不重要,您可以根据需要实现它,与硬编码值不同,通过一个 SQL 数据库和一个 ORM,以使用云上的另一个服务):
[DataContract]
public class Product
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
}
[ServiceContract]
public interface IProductsService
{
[OperationContract]
Product Get(int id);
}
现在,在您的 ASP.NET MVC 应用程序中,第一步是通过指向 WSDL 添加服务引用。这将生成代理客户端类。
接下来您可以将Unity.Mvc3 NuGet 包添加到您的 MVC 应用程序中
然后在您的Application_Start 中,您可以配置容器(显然可以将此配置外部化为单独的方法,以避免将您的 Global.asax 与它混淆):
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
var container = new UnityContainer();
container
.RegisterType<IProductsService, ProductsServiceClient>()
.Configure<InjectedMembers>()
.ConfigureInjectionFor<ProductsServiceClient>(new InjectionConstructor("*"));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
此配置中使用的IProductsService 和ProducsServiceClient 是您导入Web 服务定义时生成的代理类。
从现在开始,事情变得微不足道:
public class HomeController : Controller
{
private readonly IProductsService _productsService;
public HomeController(IProductsService productsService)
{
_productsService = productsService;
}
public ActionResult Index()
{
var product = _productsService.Get(1);
return View(product);
}
}
还有一些对应的Index视图:
@model Product
<div>
@Html.DisplayFor(x => x.Name)
</div>
正如您从这个示例中看到的,感谢IProductsService 抽象,HomeController 与服务的任何具体实现完全解耦。在今天的 Global.asax 中,您决定使用 WCF (ProductsServiceClient),但明天您可能会决定使用一些完全不同的实现。只需对 DI 容器配置进行一次更改,您就可以切换实现。由于这种弱耦合,您的控制器完全可以独立进行单元测试。
这里要意识到的重要一点是,您的业务是 Product 类和 IProductsService 接口。这反映了您的域。这是 MVC 中的 M。实现可能会发生变化,但这应该保持不变,否则您错误地确定了您的业务需求,从长远来看这可能是灾难性的。
备注:在这个例子中我没有涉及的一件事是视图模型的使用,这是非常重要的。在正确架构的 ASP.NET MVC 应用程序中,您永远不应该将域模型传递给您的视图(在本例中为 Product 类)。您应该使用视图模型。视图模型是专门为满足给定视图的要求而设计的类。因此,在现实世界的 ASP.NET MVC 应用程序中,您将拥有一个 ProductViewModel 类,Product 域模型将在控制器操作中映射到该类,并且此 ProductViewModel 将被传递给视图。这些视图模型应该在 MVC 项目中定义,因为与您的域模型相反,它们不可重用并且仅反映单个视图的特定要求。为了简化域模型和视图模型之间的映射,您可以查看AutoMapper。