在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前面项目的层次和调用关系都说明了,关系如下图 采用三层架构的时候,研究过BLL层的必要性,觉得业务逻辑完全可以在controller里实现,没有必要单独做一个项目,另一个分层多了会影响性能。后来我还是把业务逻辑独立出来,原因如下:
对于性能我觉得分层多了肯定会有影响,但是不会很大。现在硬件的更新速度远大于软件,对业务逻辑处理起来很轻松,多实例化几个类对性能影响不大。一般来说网站运行基本上是一个存数据库和取数据库的过程,业务逻辑还是比较少,只不过现在的网站使用的图片、动画更多,效果更加绚丽。我觉得网站的效率瓶颈主要出现在服务器的带宽、IO性能和存取数据库上。在代码方面能做的就是优化数据库的存取。对了一般项目来说,为了百分之几的运行效率远不如提高开发效率和更加容易的代码管理重要,能实现需求就好,运行效率是哪是大牛要做的事。 对IDAL、DAL、IBLL 、BLL这四个项目: IDAL写一个Base接口,接口中固定几个数据库操作方法,其他接口都继承自这个接口; DAL项目做个base类实现这个IDAL的base接口,其他类都继承自base类。 同样IBLL中也写一个Base接口,固定几个基本的操作方法,同样其他接口也继承自这个base接口 IBLL中也写一个base类来实现IBLL中的base接口,其他类继承自这个base类。 这里以对用户的操作来构建代码的基本模式: 一、模型 1、用户模型—User类 using System; using System.ComponentModel.DataAnnotations; namespace Ninesky.Models { /// <summary> /// 用户模型 /// <remarks> /// 创建:2014.02.02<br /> /// 修改:2014.02.05 /// </remarks> /// </summary> public class User { [Key] public int UserID { get; set; } /// <summary> /// 用户名 /// </summary> [Required(ErrorMessage="必填")] [StringLength(20,MinimumLength=4,ErrorMessage="{1}到{0}个字符")] [Display(Name="用户名")] public string UserName { get; set; } /// <summary> /// 用户组ID /// </summary> [Required(ErrorMessage = "必填")] [Display(Name = "用户组ID")] public int GroupID { get; set; } /// <summary> /// 显示名 /// </summary> [Required(ErrorMessage = "必填")] [StringLength(20, MinimumLength = 2, ErrorMessage = "{1}到{0}个字符")] [Display(Name = "显示名")] public string DisplayName { get; set; } /// <summary> /// 密码 /// </summary> [Required(ErrorMessage = "必填")] [Display(Name = "密码")] [DataType(DataType.Password)] public string Password { get; set; } /// <summary> /// 邮箱 /// </summary> [Required(ErrorMessage = "必填")] [Display(Name = "邮箱")] [DataType(DataType.EmailAddress)] public string Email { get; set; } /// <summary> /// 用户状态<br /> /// 0正常,1锁定,2未通过邮件验证,3未通过管理员 /// </summary> public int Status { get; set; } /// <summary> /// 注册时间 /// </summary> public DateTime RegistrationTime { get; set; } /// <summary> /// 上次登陆时间 /// </summary> public DateTime LoginTime { get; set; } /// <summary> /// 上次登陆IP /// </summary> public DateTime LoginIP { get; set; } public virtual UserGroup Group { get; set; } } } 这个模型类中只包含用户名、密码、用户组、显示名、邮箱等属性,纯粹是基本的账户信息,目的是让用户注册的时候尽可能的少填信息。其他信息如果需要可以再写新类与账户进行关联,用户需要的时候登录后再进行补填(如:资本资料、个人信息、联系方式等。这里先不考虑这些)。这里的显示名根据需要可以做昵称、真实姓名等来使用。 2、用户组模型—UserGroup类 using System.ComponentModel.DataAnnotations; namespace Ninesky.Models { /// <summary> /// 用户组 /// <remarks> /// 创建:2014.02.02 /// 修改:2014.02.08 /// </remarks> /// </summary> public class UserGroup { [Key] public int GroupID { get; set; } /// <summary> /// 名称 /// </summary> [Required(ErrorMessage="必填")] [StringLength(20, MinimumLength = 2, ErrorMessage = "{1}到{0}个字")] [Display(Name="名称")] public string Name { get; set; } /// <summary> /// 用户组类型<br /> /// 0普通类型(普通注册用户),1特权类型(像VIP之类的类型),3管理类型(管理权限的类型) /// </summary> [Required(ErrorMessage = "必填")] [Display(Name = "用户组类型")] public int GroupType { get; set; } /// <summary> /// 说明 /// </summary> [Required(ErrorMessage = "必填")] [StringLength(50, ErrorMessage = "少于{0}个字")] [Display(Name = "说明")] public string Description { get; set; } } } 3、用户配置模型类—UserConfig类 using System.ComponentModel.DataAnnotations; namespace Ninesky.Models { /// <summary> /// 用户配置 /// <remarks> /// 创建:2014.02.06 /// </remarks> /// </summary> public class UserConfig { [Key] public int ConfigID { get; set; } /// <summary> /// 启用注册 /// </summary> [Display(Name = "启用注册")] [Required(ErrorMessage="必填")] public bool Enabled { get; set; } /// <summary> /// 禁止使用的用户名<br /> /// 用户名之间用“|”隔开 /// </summary> [Display(Name = "禁止使用的用户名")] public string ProhibitUserName { get; set; } /// <summary> /// 启用管理员验证 /// </summary> [Display(Name = "启用管理员验证")] [Required(ErrorMessage = "必填")] public bool EnableAdminVerify { get; set; } /// <summary> /// 启用邮件验证 /// </summary> [Display(Name = "启用邮件验证")] [Required(ErrorMessage = "必填")] public bool EnableEmailVerify { get; set; } /// <summary> /// 默认用户组Id /// </summary> [Display(Name = "默认用户组Id")] [Required(ErrorMessage = "必填")] public int DefaultGroupId { get; set; } } } 二、数据存储层 在与数据库的方便有一些共同的操作,像添加、修改、删除、查询等。不想在实际写代码的时候在用户类写一遍这些东西,用户组类再写一遍、以后文章、评论都再重复写这些代码。怎么办,弄个基类。以后其他类从基类继承就把这些公共方法继承过来了。 1、IDAL项目 using System; using System.Linq; using System.Linq.Expressions; namespace Ninesky.IDAL { /// <summary> /// 接口基类 /// <remarks>创建:2014.02.03 <br /> /// 修改:2014.02.09</remarks> /// </summary> /// <typeparam name="T">类型</typeparam> public interface InterfaceBaseRepository<T> { /// <summary> /// 添加 /// </summary> /// <param name="entity">数据实体</param> /// <returns>添加后的数据实体</returns> T Add(T entity); /// <summary> /// 查询记录数 /// </summary> /// <param name="predicate">条件表达式</param> /// <returns>记录数</returns> int Count(Expression<Func<T, bool>> predicate); /// <summary> /// 更新 /// </summary> /// <param name="entity">数据实体</param> /// <returns>是否成功</returns> bool Update(T entity); /// <summary> /// 删除 /// </summary> /// <param name="entity">数据实体</param> /// <returns>是否成功</returns> bool Delete(T entity); /// <summary> /// 是否存在 /// </summary> /// <param name="anyLambda">查询表达式</param> /// <returns>布尔值</returns> bool Exist(Expression<Func<T, bool>> anyLambda); /// <summary> /// 查询数据 /// </summary> /// <param name="whereLambda">查询表达式</param> /// <returns>实体</returns> T Find(Expression<Func<T, bool>> whereLambda); /// <summary> /// 查找数据列表 /// </summary> /// <typeparam name="S">排序</typeparam> /// <param name="whereLamdba">查询表达式</param> /// <param name="isAsc">是否升序</param> /// <param name="orderLamdba">排序表达式</param> /// <returns></returns> IQueryable<T> FindList<S>(Expression<Func<T, bool>> whereLamdba, bool isAsc, Expression<Func<T, S>> orderLamdba); /// <summary> /// 查找分页数据列表 /// </summary> /// <typeparam name="S">排序</typeparam> /// <param name="pageIndex">当前页</param> /// <param name="pageSize">每页记录数</param> /// <param name="totalRecord">总记录数</param> /// <param name="whereLamdba">查询表达式</param> /// <param name="isAsc">是否升序</param> /// <param name="orderLamdba">排序表达式</param> /// <returns></returns> IQueryable<T> FindPageList<S>(int pageIndex, int pageSize, out int totalRecord, Expression<Func<T, bool>> whereLamdba, bool isAsc, Expression<Func<T, S>> orderLamdba); } } 这里定义了增、删、改、判断存在、返回模型的查询、返回集合的查询,返回分页集合的查询7个公共方法。这几个方法基本满足一般需要,特殊的方法在继承的时候再添加。 using Ninesky.Models; namespace Ninesky.IDAL { /// <summary> /// 用户接口 /// <remarks>创建:2014.02.03</remarks> /// </summary> public interface InterfaceUserRepository:InterfaceBaseRepository<User> { } } 简单吧,继承自InterfaceBaseRepository接口并传入实体类User就行了。我们在类视图中看下,是不是继承了基类的接口。 2、DAL项目 using Ninesky.Models; using System.Data.Entity; namespace Ninesky.DAL { /// <summary> /// 数据上下文 /// <remarks>创建:2014.02.03</remarks> /// </summary> public class NineskyDbContext:DbContext { public DbSet<User> Users { get; set; } public DbSet<UserGroup> UserGroups { get; set; } public DbSet<UserConfig> UserConfig { get; set; } public NineskyDbContext() : base("DefaultConnection") { } } } 下面创建一个BaseRepository类,继承自InterfaceBaseRepository并实现类其接口的方法。 using Ninesky.IDAL; using System; using System.Linq; using System.Linq.Expressions; namespace Ninesky.DAL { /// <summary> /// 仓储基类 /// <remarks>创建:2014.02.03</remarks> /// </summary> public class BaseRepository<T>: InterfaceBaseRepository<T> where T : class { protected NineskyDbContext nContext = ContextFactory.GetCurrentContext(); public T Add(T entity) { nContext.Entry<T>(entity).State = System.Data.Entity.EntityState.Added; nContext.SaveChanges(); return entity; } public int Count(Expression<Func<T, bool>> predicate) { return nContext.Set<T>().Count(predicate); } public bool Update(T entity) { nContext.Set<T>().Attach(entity); nContext.Entry<T>(entity).State = System.Data.Entity.EntityState.Modified; return nContext.SaveChanges() > 0; } public bool Delete(T entity) { nContext.Set<T>().Attach(entity); nContext.Entry<T>(entity).State = System.Data.Entity.EntityState.Deleted; return nContext.SaveChanges() > 0; } public bool Exist(Expression<Func<T, bool>> anyLambda) { return nContext.Set<T>().Any(anyLambda); } public T Find(Expression<Func<T, bool>> whereLambda) { T _entity = nContext.Set<T>().FirstOrDefault<T>(whereLambda); return _entity; } public IQueryable<T> FindList<S>(Expression<Func<T, bool>> whereLamdba, bool isAsc, Expression<Func<T, S>> orderLamdba) { var _list = nContext.Set<T>().Where<T>(whereLamdba); if (isAsc) _list = _list.OrderBy<T, S>(orderLamdba); else _list = _list.OrderByDescending<T, S>(orderLamdba); return _list; } public IQueryable<T> FindPageList<S>(int pageIndex, int pageSize, out int totalRecord, Expression<Func<T, bool>> whereLamdba, bool isAsc, Expression<Func<T, S>> orderLamdba) { var _list = nContext.Set<T>().Where<T>(whereLamdba); totalRecord = _list.Count(); if (isAsc) _list = _list.OrderBy<T, S>(orderLamdba).Skip<T>((pageIndex - 1) * pageSize).Take<T>(pageSize); else _list = _list.OrderByDescending<T, S>(orderLamdba).Skip<T>((pageIndex - 1) * pageSize).Take<T>(pageSize); return _list; } } } 代码中都是对数据库的操作。比较有看头的是这句protected NineskyDbContext nContext = ContextFactory.GetCurrentContext(); ContextFactory是一个简单工厂类,GetCurrentContext()是一个静态函数。利用简单工厂获取请求内的当前DbContext,也就是请求内的DbContext单例。先添加一个工厂类ContextFactory using System.Data.Entity; using System.Runtime.Remoting.Messaging; namespace Ninesky.DAL { /// <summary> /// 上下文简单工厂 /// <remarks> /// 创建:2014.02.05 /// </remarks> /// </summary> public class ContextFactory { /// <summary> /// 获取当前数据上下文 /// </summary> /// <returns></returns> public static NineskyDbContext GetCurrentContext() { NineskyDbContext _nContext = CallContext.GetData("NineskyContext") as NineskyDbContext; if (_nContext == null) { _nContext = new NineskyDbContext(); CallContext.SetData("NineskyContext", _nContext); } return _nContext; } } } 这里是先在CallContext中获取NineskyContext,如果为空则初始化一个NineskyContext,如果存在则直接返回。看CallContext,MSDN中讲CallContext提供对每个逻辑执行线程都唯一的数据槽,而在WEB程序里,每一个请求恰巧就是一个逻辑线程所以可以使用CallContext来实现单个请求之内的DbContext单例。 下面添加具体的仓储代码。 在DAL中再添加一个UserRepository类,继承自BaseRepository和InterfaceUserRepository。目的是继承自BaseRepository类,实现InterfaceUserRepositor接口。 using Ninesky.IDAL; using Ninesky.Models; using System.Linq; namespace Ninesky.DAL { /// <summary> /// 用户仓库 /// <remarks>创建:2014.02.03</remarks> /// </summary> class UserRepository: BaseRepository<User>, InterfaceUserRepository { } } UserRepository就直接继承了基类中的方法,基类中的方法能满足绝大部分需要,UserRepository就不用再增加函数了,其他Repository类都类似,不在贴代码了。 这里我们在建一个Repository工厂,用来返回项目中的所有Repository类。 using Ninesky.IDAL; namespace Ninesky.DAL { /// <summary> /// 简单工厂? /// <remarks>创建:2014.02.03</remarks> /// </summary> public static class RepositoryFactory { /// <summary> /// 用户仓储 /// </summary> public static InterfaceUserRepository UserRepository { get { return new UserRepository(); } } } } 以后在BLL中调用的时候就不用每次都写InterfaceUserRepository _iUserRsy = new UserRepository()了,直接写成InterfaceUserRepository _iUserRsy = RepositoryFactory.UserRepository这个东西的好处就是,以后在DAL项目中实现InterfaceUserRepository接口的类需要修改时我们可以直接创建个新类,然后RepositoryFactory类中让UserRepository属性返回新类就行了。 3、IBLL项目 namespace Ninesky.IBLL { /// <summary> /// 接口基类 /// <remarks>创建:2014.02.03</remarks> /// </summary> public interface InterfaceBaseService<T> where T : class { /// <summary> /// 添加 /// </summary> /// <param name="entity">数据实体</param> /// <returns>添加后的数据实体</returns> T Add(T entity); /// <summary> /// 更新 /// </summary> /// <param name="entity">数据实体</param> /// <returns>是否成功</returns> bool Update(T entity); /// <summary> /// 删除 /// </summary> /// <param name="entity">数据实体</param> /// <returns>是否成功</returns> bool Delete(T entity); } }
using Ninesky.Models; using System.Linq; namespace Ninesky.IBLL { /// <summary> /// 用户相关接口 /// <remarks> /// 创建:2014.02.09 /// </remarks> /// </summary> public interface InterfaceUserService:InterfaceBaseService<User> { /// <summary> /// 用户是否存在 /// </summary> /// <param name="userName">用户名</param> /// <returns>布尔值</returns> bool Exist(string userName); /// <summary> /// 查找用户 /// </summary> /// <param name="userID">用户ID</param> /// <returns></returns> User Find(int userID); /// <summary> /// 查找用户 /// </summary> /// <param name="userName">用户名</param> /// <returns></returns> User Find(string userName); /// <summary> /// 用户列表 /// </summary> /// <param name="pageIndex">页码数</param> /// <param name="pageSize">每页记录数</param> /// <param name="totalRecord">总记录数</param> /// <param name="order">排序:0-ID升序(默认),1ID降序,2注册时间升序,3注册时间降序,4登录时间升序,5登录时间降序</param> /// <returns></returns> IQueryable<User> FindPageList(int pageIndex, int pageSize, out int totalRecord,int order); } } 4、BLL项目 using Ninesky.IBLL; using Ninesky.IDAL; namespace Ninesky.BLL { /// <summary> /// 服务基类 /// <remarks>创建:2014.02.03</remarks> /// </summary> public abstract class BaseService<T> : InterfaceBaseService<T> where T : class { protected InterfaceBaseRepository<T> CurrentRepository { get; set; } public BaseService(InterfaceBaseRepository<T> currentRepository) { CurrentRepository = currentRepository; } public T Add(T entity) { return CurrentRepository.Add(entity); } public bool Update(T entity) { return CurrentRepository.Update(entity); } public bool Delete(T entity) { return CurrentRepository.Delete(entity); } } } 这个类的构造函数中要传入一个参数就是currentRepository 这个在继承的时候进行传入。这里还是看用户类。 using Ninesky.DAL; using Ninesky.IBLL; using Ninesky.Models; using System.Linq; namespace Ninesky.BLL { /// <summary> /// 用户服务类 /// <remarks> /// 创建:2014.02.12 /// </remarks> /// </summary> public class UserService:BaseService<User>,InterfaceUserService { public UserService() : base(RepositoryFactory.UserRepository) { } public bool Exist(string userName) { return CurrentRepository.Exist(u => u.UserName == userName);} public User Find(int userID) { return CurrentRepository.Find(u => u.UserID == userID); } public User Find(string userName) { return CurrentRepository.Find(u => u.UserName == userName); } public IQueryable<User> FindPageList(int pageIndex, int pageSize, out int totalRecord, int order) { switch(order) { case 0: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, true, u => u.UserID); case 1: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, false, u => u.UserID); case 2: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, true, u => u.RegistrationTime); case 3: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, false, u => u.RegistrationTime); case 4: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, true, u => u.LoginTime); case 5: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, false, u => u.LoginTime); default: return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, true, u => u.UserID); } } } } 上面这个FindPageList代码太累赘了,一时还没想到好方法。 5、总结 =================================================== FindPageList() 这个排序的方法确实不太通用,代码修改如下: 1、接口 InterfaceBaseRepository 修改两个接口方法如图红框部分。 image 2、BaseRepository类 添加OrderBy方法,代码如下: /// <summary> /// 排序 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="source">原IQueryable</param> /// <param name="propertyName">排序属性名</param> /// <param name="isAsc">是否正序</param> /// <returns>排序后的IQueryable<T></returns> private IQueryable<T> OrderBy(IQueryable<T> source, string propertyName, bool isAsc) { if (source == null) throw new ArgumentNullException("source", "不能为空"); if (string.IsNullOrEmpty(propertyName)) return source; var _parameter = Expression.Parameter(source.ElementType); var _property = Expression.Property(_parameter, propertyName); if (_property == null) throw new ArgumentNullException("propertyName", "属性不存在"); var _lambda = Expression.Lambda(_property, _parameter); var _methodName = isAsc ? "OrderBy" : "OrderByDescending"; var _resultExpression = Expression.Call(typeof(Queryable), _methodName, new Type[] { source.ElementType, _property.Type }, source.Expression, Expression.Quote(_lambda)); return source.Provider.CreateQuery<T>(_resultExpression); } 修改FindList和FindPageList方法,修改下图 image 3、修改UserService的FindPageList方法 修改后的代码如下: public IQueryable<User> FindPageList(int pageIndex, int pageSize, out int totalRecord, int order) { bool _isAsc = true; string _orderName = string.Empty; switch(order) { case 0: _isAsc = true; _orderName = "UserID"; break; case 1: _isAsc = false; _orderName = "UserID"; break; case 2: _isAsc = true; _orderName = "RegistrationTime"; break; case 3: _isAsc = false; _orderName = "RegistrationTime"; break; case 4: _isAsc = true; _orderName = "LoginTime"; break; case 5: _isAsc = false; _orderName = "LoginTime"; break; default: _isAsc = false; _orderName = "UserID"; break; } return CurrentRepository.FindPageList(pageIndex, pageSize, out totalRecord, u => true, _orderName, _isAsc); } 以上就是ASP.NET MVC5 网站开发框架模型、数据存储以及业务逻辑的相关介绍,比之前两节的内容是不是更加丰富了,希望本文可以对大家的学习有所帮助。 |
请发表评论