ASP.NET MVC5 网站开发实践(一) - 框架(续) 模型、数据存储、业务逻辑
上次搭建好了项目框架,但还是觉得不太对劲,后来才想起来没有对开发目标进行定位,这个小demo虽然不用做需求分析,但是要实现什么效果还得明确。后来想了一下就做个最简单的网站,目标定为小公司进行展示用的网站。功能有显示用的文章功能(新闻、产品展示、公司介绍等),咨询留言,评论等,另外还包括用户管理,网站设置这两个部分,算是个极简的网站吧。
目录:
前面项目的层次和调用关系都说明了,关系如下图
采用三层架构的时候,在群里跟 @怒放生命 讨论过BLL层的必要性,觉得业务逻辑完全可以在controller里实现,没有必要单独做一个项目,另一个分层多了会影响性能。后来我还是把业务逻辑独立出来,原因如下:
- 业务逻辑写进controller里代码看着比较混乱,时间久了代码容易理不清。
- 在controller里直接写逻辑重复代码会不较多,开发效率低。
- 分项目有利于代码重用,有时候可以直接拿到其他项目中稍作修改就可以用。
对于性能我觉得分层多了肯定会有影响,但是不会很大。现在硬件的更新速度远大于软件,对业务逻辑处理起来很轻松,多实例化几个类对性能影响不大。一般来说网站运行基本上是一个存数据库和取数据库的过程,业务逻辑还是比较少,只不过现在的网站使用的图片、动画更多,效果更加绚丽。我觉得网站的效率瓶颈主要出现在服务器的带宽、IO性能和存取数据库上。在代码方面能做的就是优化数据库的存取。对了一般项目来说,为了百分之几的运行效率远不如提高开发效率和更加容易的代码管理重要,能实现需求就好,运行效率是哪是大牛要做的事。
对IDAL、DAL、IBLL 、BLL这四个项目:
IDAL写一个Base接口,接口中固定几个数据库操作方法,其他接口都继承自这个接口;
DAL项目做个base类实现这个IDAL的base接口,其他类都继承自base类。
同样IBLL中也写一个Base接口,固定几个基本的操作方法,同样其他接口也继承自这个base接口
IBLL中也写一个base类来实现IBLL中的base接口,其他类继承自这个base类。
这里以对用户的操作来构建代码的基本模式:
一、模型
这里写三个模型类。打开Ninesk.Models分别添加User、UserGroup、UserConfig三个模型类。
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类
这个类注意下GroupType,这个用来对用户组进行一下分类的,方便管理,其实没什么特别的意义。我的想法是普通类型就放普通的注册用户的组,如果大的网站允许用户升级的话,限定在这个类型的用户组内。特权组可以放一些vip之类的用户组,需要管理员给予,区别普通用户组,但又没有管理权。管理类型的用户组需要后台管理员给予,可以对文章、评论、咨询进行管理。
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; }
}
}
二、数据存储层
数据存储层负责与数据库打交道,由于使用了接口产生了两个项目DAL和IDAL。IDAL是接口项目,DAL是接口的实现项目。
在与数据库的方便有一些共同的操作,像添加、修改、删除、查询等。不想在实际写代码的时候在用户类写一遍这些东西,用户组类再写一遍、以后文章、评论都再重复写这些代码。怎么办,弄个基类。以后其他类从基类继承就把这些公共方法继承过来了。
1、IDAL项目
首先打开IDAL项目,添加类InterfaceBaseRepository,代码如下。
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个公共方法。这几个方法基本满足一般需要,特殊的方法在继承的时候再添加。
还使用了泛型,在继承的时候传入实体类型就可以直接继承这些方法了。具体看下InterfaceUserRepository接口就清楚了。
using Ninesky.Models;
namespace Ninesky.IDAL
{
/// <summary>
/// 用户接口
/// <remarks>创建:2014.02.03</remarks>
/// </summary>
public interface InterfaceUserRepository:InterfaceBaseRepository<User>
{
}
}
简单吧,继承自InterfaceBaseRepository接口并传入实体类User就行了。我们在类视图中看下,是不是继承了基类的接口。
2、DAL项目
DAL项目是对IDAL项目接口的实现,项目中要创建DbContext类,对于DbContext类很多人讨论过它对数据库存取的效率,MSDN中说其是轻量的, 创建不需要很大开销,它也不是线程安全的对象,并且具有数据容器的性质(跟踪),因此很多人认为不应该将其静态化、单例化。但是对用户的单次请求来说实现DbContext唯一是合理的。 先看代码吧,非常简单。
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项目
IBLL是业务逻辑层的接口,业务逻辑层对数据库的操作上基本还是增、删、改。同样写一个基接口把这三个操作写进去,这里与IDAL思路类似。
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);
}
}
在添加一个InterfaceUserService接口,继承自InterfaceBaseService。根据需要在接口中又添加了几个方法。在这里对Find方法的名称进行统一,凡是返回实体类的名称为Find()或FindByXXX(),返回一组数据的方法名称为FindList()或FindXXXList,分页的名称格式为FindPageList()或FindxxxPageList()
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);
}
}
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项目
BLL项目中要实现InterfaceUserService接口的方法,先添加BaseService的
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、总结
今天写到这里还是在想项目间的调用实现,写了两个base接口、两个base类,以后其他的类都从它们继承,写法都很类似。下次可以开始做界面了,在Ninesky.Web项目中基本上是通过IBLL,BLL跟数据进行打交道了。
===================================================
FindPageList() 这个排序的方法确实不太通用,参考@jingming290 提供的代码修改如下:
1、接口 InterfaceBaseRepository
修改两个接口方法如图红框部分。
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方法,修改下图
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);
}
感谢@jingming290!
ASP.NET MVC5 网站开发实践(一) - 框架(续) 模型、数据存储、业务逻辑的更多相关文章
- ASP.NET MVC5 网站开发实践(一) - 项目框架
前几天算是开题了,关于怎么做自己想了很多,但毕竟没做过项目既不知道这些想法有无必要,也不知道能不能实现,不过邓爷爷说过"摸着石头过河"吧.这段时间看了一些博主的文章收获很大,特别是 ...
- ASP.NET MVC5 网站开发实践(一) - 项目框架(转)
前几天算是开题了,关于怎么做自己想了很多,但毕竟没做过项目既不知道这些想法有无必要,也不知道能不能实现,不过邓爷爷说过“摸着石头过河”吧.这段时间看了一些博主的文章收获很大,特别是@kencery,依 ...
- ASP.NET MVC5 网站开发实践(二) Member区域–管理列表、回复及删除
本来想接着上次把这篇写完的,没想到后来工作的一些事落下了,放假了赶紧补上. 目录: ASP.NET MVC5 网站开发实践 - 概述 ASP.NET MVC5 网站开发实践(一) - 项目框架 ASP ...
- ASP.NET MVC5 网站开发实践(二) Member区域–我的咨询列表及添加咨询
上次把咨询的架构搭好了,现在分两次来完成咨询:1.用户部分,2管理部分.这次实现用户部分,包含两个功能,查看我的咨询和进行咨询. 目录: ASP.NET MVC5 网站开发实践 - 概述 ASP.NE ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 咨询管理的架构
咨询.留言.投诉等功能是网站应具备的基本功能,可以加强管理员与用户的交流,在上次完成文章部分后,这次开始做Member区域的咨询功能(留言.投诉都是咨询).咨询跟文章非常相似,而且内容更少.更简单. ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 修改及删除文章
上次做了显示文章列表,再实现修改和删除文章这部分内容就结束了,这次内容比较简单,由于做过了添加文章,修改文章非常类似,就是多了一个TryUpdateModel部分更新模型数据. 目录: ASP.N ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 全部文章列表
显示文章列表分两块,管理员可以显示全部文章列表,一般用户只显示自己的文章列表.文章列表的显示采用easyui-datagrid.后台需要与之对应的action返回json类型数据 目录 ASP.N ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 添加文章
上次把架构做好了,这次做添加文章.添加文章涉及附件的上传管理及富文本编辑器的使用,早添加文章时一并实现. 要点: 富文本编辑器采用KindEditor.功能很强大,国人开发,LGPL开源,自己人的好东 ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 文章管理架构
上次把member的用户部分完成,现在开始做文章管理部分.文章部分根据涉及显示现实文章列表,发布文章,修改文章,删除文章等功能.最终的实现目标是使用权限来控制用户是否能进行相应操作,管理员权限的会显示 ...
随机推荐
- ubuntu常用命令大全
一.文件/文件夹管理 ls 列出当前目录文件(不包括隐含文件) ls -a 列出当前目录文件(包括隐含文件) ls -l 列出当前目录下文件的详细信息 cd .. 回当前目录的上一级目录 cd - 回 ...
- Leetcode Maximum Product Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest ...
- ubuntu server 搭建自己的个人博客及其他网站
一, 安装apache2服务器 sudo apt-get install apache2 二,安装mysql服务器 sudo apt-get install mysql-server 此时会提示输入M ...
- bzoj1001最小割
听说最大流过不去??? dinic果然神了,1000,000的点都能过(主要是比较稀疏) #include <cstdio> #define INF 9223372036854775807 ...
- Python框架之Tornado(二)请求阶段
概述 上图是tornado程序启动以及接收到客户端请求后的整个过程,对于整个过程可以分为两大部分: 启动程序阶段,又称为待请求阶段(上图1.2所有系列和3.0) 接收并处理客户端请求阶段(上图3系列) ...
- 百度Ueditor配置问题
var URL = window.UEDITOR_HOME_URL || getUEBasePath(); 在ueditor.config.js中这一句是配置编辑器的网站根目录位置的,建议不要改,网上 ...
- WebService创建与使用
因为项目中需要实现客户端与服务器端的数据交换,以及获取服务器端其他程序的分析结果,所以对WebService做了些简单的了解,现记录如下: 一.WebService程序编写 1. 在VS中新建空白网 ...
- 奇怪的UnexpectedRollbackException异常
今天在使用一个原来常用的功能的时候,突然发现在某些场景下会报异常,内容如下: 通过断点调试发现一路都很顺畅,就是在从controller层返回前段的时候会报该异常,没办法,只能通过排除法定位问题,后来 ...
- python中实现定时器Timer
实现定时器最简单的办法是就是循环中间嵌time.sleep(seconds), 这里我就不赘述了 # encoding: UTF-8 import threading #Timer(定时器)是Thre ...
- 玩转JavaScript OOP[0]——基础类型
前言 long long ago,大家普遍地认为JavaScript就是做一些网页特效的.处理一些事件的.我身边有一些老顽固的.NET程序员仍然停留在这种认知上,他们觉得没有后端开发肯定是构建不了系统 ...