本篇文章介绍使用StructureMap依赖注入扩展ASP.NET MVC三层架构中的Service层,Service层是业务逻辑层,Service层就需要注入Repository了,但是本片文章暂时不介绍StructureMap的配置,遇到注入的地方只是先写出来声明一下,下一篇文章将StructureMap的配置。
MVC中的三层结构很重要的一层就是业务逻辑层,这一层是Controller和Repository之间的桥梁,我们最好不要在Controller中写太多的业务逻辑,有可能的话不在Controller中写任何的业务逻辑代码,Controller只负责跳转,这样我们就需要Service层来完成所有的业务逻辑。
这一层涉及到TYStudioDemo.DTO,TYStudioDemo.Interfaces这两个工程,所以也一并介绍这两个工程,还有就是异常的处理,首先我们顶一个规则,不在Service层和Repository中Catch任何的异常,你可以增加一些友好异常信息,但是绝对不能Catch住,Catch是Controller层的事情,我们在那里处理异常并记录日志。
数据交互层TYStudioDemo.DTO工程
这个工程目前天屹的构架只把ViewModel放在这里面,DTO是Data Transfer Object简称翻译过来就是数据交互对象,所以我觉得这一层的命名可能有一些不合适,如果你觉得他长得太丑了,自己起一个漂亮的名字吧。
SupplierViewModel
这个类和JAVA的POJO类很想只有一些属性的和Set,Get方法,基本的内容是Supplier表中的各个字段名字,当然你可以根据具View具体的显示需求随意扩展。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.ComponentModel.DataAnnotations;
- namespace TYStudioDemo.DTO
- {
- public class SupplierViewModel
- {
- [Key]
- [Display(Name = "编号")]
- public int SupplierID { set; get; }
- [Required(ErrorMessage = "请填写公司名称")]
- [Display(Name = "公司名称")]
- public string CompanyName { set; get; }
- [Display(Name = "联系名称")]
- public string ContactName { set; get; }
- [Display(Name = "联系标题")]
- public string ContactTitle { set; get; }
- [Display(Name = "地址")]
- public string Address { set; get; }
- [Display(Name = "城市")]
- public string City { set; get; }
- [Display(Name = "地区")]
- public string Region { set; get; }
- [Display(Name = "邮编")]
- public string PostalCode { set; get; }
- [Display(Name = "国家")]
- public string Country { set; get; }
- [Display(Name = "电话")]
- public string Phone { set; get; }
- [Display(Name = "传真")]
- public string Fax { set; get; }
- [Display(Name = "公司网站")]
- public string HomePage { set; get; }
- }
- }
代码中的[Key],[Diaplay]等标识属性,天屹这里就不在具体介绍,可以自行百度一下,如果再不明白,留下评论也可以。
顶级接口ITYService
这里和repository一样,同样定义了一个顶级接口ITYService。作用和ITYRepository是一样的,都是声明一些通用方法,你可以根据自己的实际需求添加相应的公共方法,先来看一下这个接口的代码吧。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace TYStudioDemo.Interfaces
- {
- /// <summary>
- /// Service的顶级接口,所有Service类都要实现此接口
- /// 声明Service公共的方法,根据需求可以加入自己想要的方法
- /// 例如:管理系统可以加入CRUD的方法
- ///
- /// 本教程只声明了一个将Entity转化为DTO的方法
- /// </summary>
- public interface ITYService
- {
- /// <summary>
- /// 将Entity转化成ViewModel对象
- /// 这样做是为了使View,也就是UI层和Model database层分开,降低耦合度
- /// 因为所有View都将使用ViewModel,而不是Entity
- ///
- /// 这里指定object为所有的参数和返回类型
- /// 因为虽有的对象都是从object继承下来的
- /// 将来你传进来什么他就是什么~是不是很酷,那句话叫什么来着:父类引用指向子类对象
- /// </summary>
- /// <param name="entity"></param>
- /// <returns></returns>
- object ConvertToViewModel(object entity);
- }
- }
你可能会有一个问题,业务逻辑不会像repository那样会有通用的那些曾删改查,也不会像repository那样只处理一个表,一个service通常会处理多个表多个entity,而且实现也是不一样的,所以注意一点这个顶级接口并不是必须的,如果你觉得他很碍事完全可以删掉它。但是天屹这个系统这个系统有一个DTO(数据交互层,可能命名不是很合适,暂且叫他这个名字吧)工程,这里面放了一堆的ViewModel,这些ViewModel是直接给前台的View用的(强类型会使用到),所以我觉得每个Service都要有一个方法ConvertToViewModel的,你可以在上面代码注释中找到这个方法的具体用途。
上面说了Service层不想repository那样会有统一的实现,所以没有想TYRepository那样为他单独实现一个类。
ISupplierService和SupplierService介绍
ISupplierService接口
先粗略的看一下ISupplierService代码:(为什么天屹每次都先贴截图和代码呢,我觉得有了一个实际的东西,你再去理解他就会变得容易多了)
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using TYStudioDemo.DTO;
- namespace TYStudioDemo.Interfaces
- {
- /// <summary>
- /// 声明Supplier供应商独有的业务逻辑方法
- ///
- /// 本教程声明了CRUD和Search方法
- /// </summary>
- public interface ISupplierService : ITYService
- {
- IEnumerable<SupplierViewModel> GetAll();
- SupplierViewModel GetByID(int ID);
- void Create(SupplierViewModel dto);
- void Delete(int ID);
- void Update(SupplierViewModel dto);
- IEnumerable<SupplierViewModel> SearchByCriteria(string companyName);
- }
- }
看到代码是不是有一种很熟悉的感觉,没错他和ITYRepository很像,原因上面的ITYService已经介绍了,这也是你可以删掉ITYService的原因,你可以把ConvertToViewModel声明在这里。没有什么需要特别说明的,这里就是一些曾删改查的方法。
SupplierService实现类
首先是代码:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using TYStudioDemo.Interfaces;
- using TYStudioDemo.DTO;
- using TYStudioDemo.Models;
- using TYStudioDemo.Commons;
- namespace TYStudioDemo.Services
- {
- public class SupplierService : ISupplierService
- {
- ISupplierRepository<Supplier> _supplierRepository;
- IProductRepository<Product> _productRepository;
- public SupplierService(ISupplierRepository<Supplier> supplierRepotitory,
- IProductRepository<Product> productRepository)
- {
- _supplierRepository = supplierRepotitory;
- _productRepository = productRepository;
- }
- public IEnumerable<SupplierViewModel> GetAll()
- {
- List<SupplierViewModel> list = new List<SupplierViewModel>();
- foreach(Supplier entity in _supplierRepository.GetAll())
- {
- list.Add(ConvertToViewModel(entity) as SupplierViewModel);
- }
- return list.AsEnumerable<SupplierViewModel>();
- }
- public SupplierViewModel GetByID(int ID)
- {
- return ConvertToViewModel(_supplierRepository.Single(e => e.SupplierID == ID)) as SupplierViewModel;
- }
- public void Create(SupplierViewModel vModel)
- {
- try
- {
- Supplier supplier = new Supplier();
- supplier.SupplierID = vModel.SupplierID;
- supplier.Address = vModel.Address;
- supplier.City = vModel.City;
- supplier.CompanyName = vModel.CompanyName;
- supplier.ContactName = vModel.ContactName;
- supplier.ContactTitle = vModel.ContactTitle;
- supplier.Country = vModel.Country;
- supplier.Fax = vModel.Fax;
- supplier.HomePage = vModel.HomePage;
- supplier.Phone = vModel.Phone;
- supplier.PostalCode = supplier.PostalCode;
- supplier.Region = vModel.Region;
- _supplierRepository.Create(supplier);
- //为了显示事务的处理,这里添加一个临时的Product
- //由于Product数据并没有通过vModel传递过来,此处只是测试数据。
- Product product = new Product();
- //给product属性赋值
- //其他的属性略去,只写一个必须的名称
- product.ProductName = "testProductName";
- //虽然还没有调用datacontext的保存方法,没有在数据库中生成supplierid,
- //但是EntityFramework内部会指定一个新的supplierid,所以这里supplierid是有值的
- product.SupplierID = supplier.SupplierID;
- _productRepository.Create(product);
- //TYEntities.Current保存修改数据
- //当处理多个表时,只需要调用一次SaveChanges(),所以都在一个Transaction里面。
- TYEntities.Current.SaveChanges();
- }
- catch (Exception ex)
- {
- //虽然此处谢了try catch语句,但是我们并不处理这里的异常,继续throw到Controller层
- //所有异常都在Controller层catch,返回错误信息。
- //此处的try catch不是必须的,当让如果你想完善异常信息的处理,这里可以自定义你的异常信息。
- Exception exception = new Exception("添加Supplier出错" + ex.Message);
- //抛到Controller层
- throw exception;
- }
- }
- public void Delete(int ID)
- {
- Supplier entity = _supplierRepository.Single(e => e.SupplierID == ID);
- _supplierRepository.Delete(entity);
- TYEntities.Current.SaveChanges();
- }
- public void Update(SupplierViewModel vModel)
- {
- Supplier entity = _supplierRepository.Single(e => e.SupplierID == vModel.SupplierID);
- entity.SupplierID = vModel.SupplierID;
- entity.Address = vModel.Address;
- entity.City = vModel.City;
- entity.CompanyName = vModel.CompanyName;
- entity.ContactName = vModel.ContactName;
- entity.ContactTitle = vModel.ContactTitle;
- entity.Country = vModel.Country;
- entity.Fax = vModel.Fax;
- entity.HomePage = vModel.HomePage;
- entity.Phone = vModel.Phone;
- entity.PostalCode = entity.PostalCode;
- entity.Region = vModel.Region;
- TYEntities.Current.SaveChanges();
- }
- public object ConvertToViewModel(object entity)
- {
- Supplier supplier = (Supplier)entity;
- SupplierViewModel vModel = new SupplierViewModel();
- vModel.SupplierID = supplier.SupplierID;
- vModel.Address = supplier.Address;
- vModel.City = supplier.City;
- vModel.CompanyName = supplier.CompanyName;
- vModel.ContactName = supplier.ContactName;
- vModel.ContactTitle = supplier.ContactTitle;
- vModel.Country = supplier.Country;
- vModel.Fax = supplier.Fax;
- vModel.HomePage = supplier.HomePage;
- vModel.Phone = supplier.Phone;
- vModel.PostalCode = supplier.PostalCode;
- vModel.Region = supplier.Region;
- return vModel;
- }
- /// <summary>
- /// 搜索方法,参数根据需求可以自由添加
- /// </summary>
- /// <param name="companyName"></param>
- /// <returns></returns>
- public IEnumerable<SupplierViewModel> SearchByCriteria(string companyName)
- {
- //此处实现你的搜索
- throw new NotImplementedException();
- }
- }
- }
这个类主要有一个方法需要介绍一下Create(SupplierViewModel vModel),其他的方法相信你一看代码就会明白,还是先看一下这个方法,方法中介绍Transaction事务和异常的处理方法,详细的解释我写在了注释里面,见上面的注释。
总结:
MVC中的Service业务逻辑层中不要Catch任何异常,抛到Controller层。SupplierService层中的两个repository构造方法是StructureMap依赖注入需要用的。以后会具体介绍。
下一篇文章我们来看UI层,Controller和View。
为什么大部分代码都放在service层里。这里不大明白。 repository层里没什么东西。
repository层难道不才是数据持久层吗?
service(服务)是业务逻辑层不直接和数据库打交道,repository负责和数据库和model数据库的持久。这样将来数据库由于一些原因更换,service层几乎可以不做修改,只修改repository的实现就可以了。