你的位置:首页 > ASP.net教程

[ASP.net教程]从头开始一步一步实现EF6+Autofac+MVC5+Bootstarp极简的实现前后台ajax表格展示及分页实现


本来是想试着做一个简单OA项目玩玩的,真是不做不知道,一做吓死人,原来以为很简单的事情,但是做起来不是忘这就是忘那的,有的技术还得重新温习。所以还是得记录。免得哪天电脑挂了,就全没有了。

开始是看了园子里何镇汐的一系列文章,写的太好了,只看了几篇就有想写代码的冲动,很大一部分都是搬他的东西。但是我还是领误不了DDD,所以先就着三层搞搞。

我搞了两个解决方案,一个本着是想做框架,把有通用的封装了,以后要用就引dll,由于太枯燥,另一个就是想做个玩具项目,两边轮流搞搞

先是dll部分的,当然很多都没实现反正是自己的,以后再慢慢补,我这里东西很少,所以没用文件夹之类的,全部直接一个命名空间

先是autofac封装类,只是简单封装了一下,做玩具够用,跟上一篇博客一样的,这里不说了

using Autofac;using System;namespace MTF.Domain{  public class DependencyContainer  {    public static IContainer Container { get { return container; } }    public static IContainer container;    private DependencyContainer() { }    public static void Initializer(Action<ContainerBuilder> action)    {      ContainerBuilder builder = new ContainerBuilder();      if (action != null)      {        action(builder);        container = builder.Build();      }    }    private static void Error()    {      if (container == null)        throw new InvalidOperationException("容器没有初始化");      else        return;    }    public static TService Resolve<TService>()    {      return container.Resolve<TService>();    }  }}

Dependency类代码

 

BaseEntity 的设计是参考何兄的,大家可以去看他的文章,写的太好了,这里几乎是搬的

using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations;namespace MTF.Domain{  public abstract class BaseEntity<TKey> : IEntity<TKey>  {    #region 初始化    public TKey GId { get; private set; }    protected BaseEntity(TKey id)    {      this.GId = id;      validationRules = new List<IValidationRule>();    }     #endregion    #region 实体相等性比较    public override bool Equals(object obj)    {      if (obj == null)        return false;      if (!(obj is BaseEntity<TKey>))        return false;      return this == (BaseEntity<TKey>)obj;    }    public override int GetHashCode()    {      return this.GId.GetHashCode();    }    public static bool operator ==(BaseEntity<TKey> entity1, BaseEntity<TKey> entity2)    {      if ((object)entity1 == null || (object)entity2 == null)        return false;      if (entity1.GId == null)        return false;      if (entity1.GId.Equals(default(TKey)))        return false;      return entity1.GId.Equals(entity2.GId);    }    public static bool operator !=(BaseEntity<TKey> entity1, BaseEntity<TKey> entity2)    {      return !(entity1 == entity2);    }    #endregion    #region 验证、验证规则添加及验证处理    private List<IValidationRule> validationRules;//验证结果集合    private ICollection<ValidationResult> validationResults;//验证规则集合    private IValidationHandler validationHandler;//验证处理器    private void DefaultValidtae()    {      validationResults = new List<ValidationResult>();      ValidationContext context = new ValidationContext(this, null, null);      Validator.TryValidateObject(this, context, validationResults, true);//默认验证实体特性注解      if (validationRules.Count > 0)//如果有其它验证规则      {        validationRules.ForEach(o =>        {          if (o.Validate() == ValidationResult.Success || o.Validate() == null)            return;          //其它规则不通过时          validationResults.Add(o.Validate());//添加到验证结果集合        });      }    }    /// <summary>    /// 验证标识    /// </summary>    public virtual bool IsValid { get      {        DefaultValidtae();        return validationResults.Count <= 0;      } }    /// <summary>    /// 添加验证规则    /// </summary>    /// <param name="rule"></param>    public void AddValidationRule(IValidationRule rule)    {      if (rule == null)        return;      validationRules.Add(rule);    }    /// <summary>    /// 默认验证当前实体方法    /// </summary>    public virtual void Validate()    {      if (IsValid)        return;      //验证不通过时            try      {        validationHandler = DependencyContainer.Resolve<IValidationHandler>();//设置验证处理器      }      catch      {        throw new InvalidOperationException("当前实体没有通过验证,但未设置验证处理器,请检查容器是否注入");      }            validationHandler.Handle(validationResults);//处理验证结果          }     #endregion  }}

View Code

Repository数据操作接口,这里也是搬的,但有一点不一样,考虑到排序我不想用Guid类型的,所以没有约束Entity必须实现IBaseEntity接口或者聚合根,因为我用的三层,没用聚合根,再者我在后面的Service层想用到分页排序,看到后面应该就能理解的。但是我并不知道这样对不对

IUnitOfWork只搞了一个方法,以后再搞

using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;namespace MTF.Domain{   public interface IRepository<TEntity,TKey> where TEntity:class  {    /// <summary>    /// 添加单个实体    /// </summary>    /// <param name="entity"></param>    void Add(TEntity entity);    /// <summary>    /// 添加实体集合    /// </summary>    /// <param name="entities"></param>    void Add(IEnumerable<TEntity> entities);    /// <summary>    /// 修改    /// </summary>    /// <param name="entity"></param>    void Update(TEntity entity);    /// <summary>    /// 根据Id删除实体    /// </summary>    /// <param name="id"></param>    void Remove(TKey id);    /// <summary>    /// 删除实体    /// </summary>    /// <param name="entity"></param>    void Remove(TEntity entity);    /// <summary>    /// 查找实体列表    /// </summary>    /// <returns></returns>    List<TEntity> FindAll();    /// <summary>    /// 查找实体列表    /// </summary>    /// <returns></returns>    IQueryable<TEntity> Find();    /// <summary>    /// 查找实体列表    /// </summary>    /// <param name="ids"></param>    /// <returns></returns>    //List<TEntity> Find(IEnumerable<TKey> ids);    /// <summary>    /// 根据Id查找实体    /// </summary>    /// <param name="id"></param>    /// <returns></returns>    TEntity Find(params object[] id);    /// <summary>    /// 判断实体是否存在    /// </summary>    /// <param name="predicate"></param>    /// <returns></returns>    bool Exists(Expression<Func<TEntity, bool>> predicate);    /// <summary>    /// 索引器查找    /// </summary>    /// <param name="id"></param>    /// <returns></returns>    TEntity this[TKey id] { get; }    /// <summary>    /// 保存    /// </summary>    void Save();    /// <summary>    /// 获取工作单元    /// </summary>    /// <returns></returns>    IUnitOfWork GetUnitOfWork();    IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> where);      }}

View Code
using System;namespace MTF.Domain{  public interface IUnitOfWork:IDisposable  {    void Commit();  }}

View Code
using System.Collections.Generic;using System.ComponentModel.DataAnnotations;namespace MTF.Domain{  public interface IValidationHandler  {    void Handle(ICollection<ValidationResult> validationResults);  }}

View Code
using System.ComponentModel.DataAnnotations;namespace MTF.Domain{  public interface IValidationRule  {    ValidationResult Validate();  }}

View Code

 

Domain现在只完成了这么多,其它的接口还没搞,还是以后再搞,懒的。

后面是EF部分了,引用Domain层

简单的工作单元

using MTF.Domain;using System.Data.Entity;using System.Data.Entity.Infrastructure;using System.Data.Entity.Validation;namespace MTF.EFDatas{  public class EFUnitOfWork :DbContext, IUnitOfWork  {    protected EFUnitOfWork(string connection) : base(connection) { }    public void Commit()    {      try      {        SaveChanges();      }      catch(DbUpdateConcurrencyException)      {        throw;      }      catch(DbEntityValidationException)      {        throw;      }    }      }}

EFUnitOfWork

简单的EF数据操作,还是以后慢慢再搞

using MTF.Domain;using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;namespace MTF.EFDatas{  public class EFRepository<TEntity,TKey>: IRepository<TEntity,TKey> where TEntity:class  {    protected EFUnitOfWork unitOfWork { get; private set; }    public EFRepository()    {      this.unitOfWork = DependencyContainer.Resolve<EFUnitOfWork>();    }    public void Add(TEntity entity)    {      unitOfWork.Set<TEntity>().Add(entity);    }    public void Add(IEnumerable<TEntity> entities)    {      unitOfWork.Set<TEntity>().AddRange(entities);    }    public void Update(TEntity entity)    {      unitOfWork.Entry(entity).State = System.Data.Entity.EntityState.Modified;    }    public void Remove(TKey id)    {      unitOfWork.Set<TEntity>().Remove(Find(id));    }    public void Remove(TEntity entity)    {      unitOfWork.Set<TEntity>().Remove(entity);    }    public List<TEntity> FindAll()    {      return Find().ToList();    }    public IQueryable<TEntity> Find()    {      return unitOfWork.Set<TEntity>();    }    //public List<TEntity> Find(IEnumerable<TKey> ids)    //{    //  if (ids == null)    //    return null;    //  return Find().Where(o => ids.Contains(o.GId)).ToList();    //}    public TEntity Find(params object[] id)    {      return unitOfWork.Set<TEntity>().Find(id);    }    public IQueryable<TEntity> Find(Expression<Func<TEntity,bool>> where)    {      return Find().Where(where);    }    public bool Exists(Expression<Func<TEntity, bool>> predicate)    {      return unitOfWork.Set<TEntity>().Any(predicate);    }    public TEntity this[TKey id] { get { return Find(id); } }    public void Save()    {      unitOfWork.Commit();    }    public IUnitOfWork GetUnitOfWork()    {      return unitOfWork;    }      }  }

EFRepository

 

这里我自己写了一个分页类EF专用的,因为不想一定要用ID分类,所以加了个泛型类型,专门用于排序用的,本来想着这个类是专门给service用的,不应该用public的,但是想着以后别的地方可能会用到,所以还是公共了

using System;using System.Data.Entity;using System.Linq;using System.Linq.Expressions;namespace MTF.EFDatas{  /// <summary>  /// 分页  /// </summary>  /// <typeparam name="TEntity"></typeparam>  /// <typeparam name="TOrder">要排序的类型</typeparam>  public class Pager<TEntity,TOrder> where TEntity : class  {    public Pager(DbContext eow)    {      this.unitOfWork = eow;      this.total = eow.Set<TEntity>().Count();      Initia(1, 20);    }    private DbContext unitOfWork;    public int PageIndex { get { return pageIndex; } }    private int pageIndex;    public int PageCount { get { return pageCount; } }    private int pageCount;    public int Total { get { return total; } }    private int total;    public int PageSize { get { return pageSize; } }    private int pageSize;    public int Skip { get { return skip; } }    private int skip;    public int Take { get { return take; } }    private int take;    private void Initia(int index, int size)    {      if (index < 1)        index = 1;      if (size >= total)      {        pageIndex = 1; pageCount = 1; pageSize = total; skip = 0; take = pageSize;      }      if (size < total)      {        int n = total % size; int x = total / size;        if (n == 0)        {          pageSize = size;          pageCount = x;          if (index > pageCount)          {            index = PageCount;            pageIndex = index;          }          pageIndex = index;          skip = (pageIndex - 1) * size;          take = size;        }        else        {          pageCount = x + 1;          if (index > pageCount)          {            pageIndex = pageCount;          }          else          {            pageIndex = index;          }          if (pageIndex == pageCount)          {            pageIndex = PageCount;            pageSize = n;            skip = (pageIndex - 1) * size;            take = n;          }          else          {            pageSize = size;            skip = (pageIndex - 1) * size;            take = size;          }        }      }    }    public void SetPageSize(int size)    {      pageSize = size;      Initia(PageIndex, PageSize);    }    public IQueryable<TEntity> GetPageList(Expression<Func<TEntity, TOrder>> order)    {      return unitOfWork.Set<TEntity>().OrderBy(order).Skip(Skip).Take(Take);    }    public IQueryable<TEntity> GetPageList(int pageIndex, Expression<Func<TEntity, TOrder>> order)    {      this.pageIndex = pageIndex;      Initia(PageIndex, PageSize);      return GetPageList(order);    }    public IQueryable<TEntity> GetPageList(int pageIndex, int pageSize, Expression<Func<TEntity, TOrder>> order)    {      this.pageIndex = pageIndex;      this.pageSize = pageSize;      Initia(PageIndex, PageSize);      return GetPageList(order);    }  }}

自己写的Pager

 

直接给代码先,都是很简单,不知道合不合理,这里用到了Pager类,当作属性使用,客户端得到Service就可以使用它的,它们共用一个工作单元

using MTF.Domain;using System;using System.Collections.Generic;using System.Data.Entity;using System.Linq;using System.Linq.Expressions;namespace MTF.EFDatas{  public class BaseService<TEntity,TKey,TOrderPage> where TEntity:class  {    private IRepository<TEntity, TKey> Repository;    private Pager<TEntity, TOrderPage> pager;    public Pager<TEntity,TOrderPage> Pager { get { return pager; } }    public BaseService()    {      Repository = DependencyContainer.Resolve<IRepository<TEntity, TKey>>();      pager = new Pager<TEntity, TOrderPage>(DependencyContainer.Resolve<EFUnitOfWork>());    }            public void Add(TEntity entity)    {      Repository.Add(entity);    }    public void Add(IEnumerable<TEntity> entities)    {      Repository.Add(entities);    }    public void Update(TEntity entity)    {      Repository.Update(entity);    }    public void Remove(TKey id)    {      Repository.Remove(id);    }    public void Remove(TEntity entity)    {      Repository.Remove(entity);    }    public List<TEntity> FindAll()    {      return Repository.FindAll();    }    public IQueryable<TEntity> Find()    {      return Repository.Find();    }    //public List<TEntity> Find(IEnumerable<TKey> ids)    //{    //  return Repository.Find(ids);    //}    public TEntity Find(params object[] id)    {      return Repository.Find(id);    }    public IQueryable<TEntity> Find(Expression<Func<TEntity,bool>> where)    {      return Find().Where(where);    }    public bool Exists(Expression<Func<TEntity, bool>> predicate)    {      return Repository.Exists(predicate);    }    public TEntity this[TKey id] { get { return Find(id); } }    public void Save()    {      Repository.Save();    }  }}

BaseService

由于没有写测试代码,写到这里的时候 我都有点迫不及待想试试了,所以就先到此生成。另外写一个MVC试试呗,搞个三层玩玩

DataModel层添加上面的DLL引用,我叫MTF指的是My Test Frame呵呵,不会英语,反正是搞的玩。

安装EF和AUTOFAC包开搞,先搞个BaseEntity,由于我int 类型不会初始化的时候赋值,

但是又不想GUID来排序,所以我想的办法是实体接口的时候,给的GId的属性名,主键我还是用的int类型,Guid初始化的时候赋值,它同样是唯一的,方便内存操作,int类型做自增的主键,在插入数据库后才有值。

做了三个类,用户,权限和角色,还有三个中间类,以配多对多关系,本着学习的精神,我用的CodeFirst和F..Api(不会定这个单词了,晕)方式配置映射

一样的简单玩玩的,可能还没有注释,直接上了

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using MTF.Domain;namespace OADemo.DataModel.Domains{  public class EntityBase:BaseEntity<Guid>  {    protected EntityBase(Guid id) : base(id) { }//guid类型的属性以后应该可以用到,它是在初始化的时候赋值的,内存中好找    public EntityBase() : this(Guid.NewGuid())//int类型映射主键,插入数据库时才有值,方便数据库不方便内存操作    {      SubTime = DateTime.Now;//提交时间为当前时间      IsDeleted = false;//这个好像有点多余,默认应该就是false,但不确定,所以加上了    }    //子类共有的属性    public int Id { get; set; }    public bool IsDeleted { get; set; }    public DateTime SubTime { get; set; }    public string Remark { get; set; }  }}

EntityBase
using System.Collections.Generic;namespace OADemo.DataModel.Domains{  public class UserEntity:EntityBase  {    public ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();//导航属性    public ICollection<UserPermission> UserPermissions { get; set; } = new List<UserPermission>();//导航属性    public string Name { get; set; }    public int Age { get; set; }    public string Email { get; set; }    public string RealName { get; set; }  }  public class RoleEntity:EntityBase  {    public ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();    public ICollection<RolePermission> RolePermissions { get; set; } = new List<RolePermission>();    public string Name { get; set; }    public string Description { get; set; }  }  public class Permission:EntityBase  {    public ICollection<UserPermission> UserPermissions { get; set; } = new List<UserPermission>();    public ICollection<RolePermission> RolePermissions { get; set; } = new List<RolePermission>();    public string Name { get; set; }    public string Url { get; set; }    public string HttpMethod { get; set; }  }  public class UserRole:EntityBase  {    public UserEntity User { get; set; }//显示外键属性    public RoleEntity Role { get; set; }    public int UserId { get; set; }    public int RoleId { get; set; }  }  public class UserPermission:EntityBase  {    public UserEntity User { get; set; }    public int UserId { get; set; }    public Permission Permission { get; set; }    public int PermissionId { get; set; }  }  public class RolePermission:EntityBase  {    public RoleEntity Role { get; set; }    public int RoleId { get; set; }    public Permission Permission { get; set; }    public int PermissionId { get; set; }  }}

Entities

配置映射

using System.Data.Entity.ModelConfiguration;namespace OADemo.DataModel.Domains.Maps{  public class EntityMap<T>:EntityTypeConfiguration<T> where T:EntityBase  {    public EntityMap()    {      this.HasKey(o => o.Id);//指定主键,这个类没多大必要,代码不多也是本着学习的目的玩      this.Ignore(o => o.IsValid);    }  }}

EntityMap

 

namespace OADemo.DataModel.Domains.Maps{  public class UserRoleMap:EntityMap<UserRole>  {    public UserRoleMap()    {      //指定导航属性和外键,配置多对多关系      this.HasRequired(o => o.User).WithMany(o => o.UserRoles).HasForeignKey(o => o.UserId);      this.HasRequired(o => o.Role).WithMany(o => o.UserRoles).HasForeignKey(o => o.RoleId);    }  }}

UserRoleMap
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace OADemo.DataModel.Domains.Maps{  public class UserPermissionMap:EntityMap<UserPermission>  {    public UserPermissionMap()    {      this.HasRequired(o => o.User).WithMany(o => o.UserPermissions).HasForeignKey(o => o.UserId);      this.HasRequired(o => o.Permission).WithMany(o => o.UserPermissions).HasForeignKey(o => o.PermissionId);    }  }}

UserPermissionMap
namespace OADemo.DataModel.Domains.Maps{  public class RolePermissionMap:EntityMap<RolePermission>  {    public RolePermissionMap()    {      this.HasRequired(o => o.Role).WithMany(o => o.RolePermissions).HasForeignKey(o => o.RoleId);      this.HasRequired(o => o.Permission).WithMany(o => o.RolePermissions).HasForeignKey(o => o.PermissionId);    }  }}

RolePermissionMap
namespace OADemo.DataModel.Domains.Maps{  public class UserMap:EntityMap<UserEntity>  {    public UserMap()    {      this.Property(o => o.Name).IsRequired().HasMaxLength(30);      this.Property(o => o.Age).IsRequired();      this.Property(o => o.Email).IsRequired().HasMaxLength(100);      this.Property(o => o.RealName).HasMaxLength(30);    }  }}

UserMap
namespace OADemo.DataModel.Domains.Maps{  public class RoleMap:EntityMap<RoleEntity>  {    public RoleMap()    {      this.Property(o => o.Name).IsRequired().HasMaxLength(50);      this.Property(o => o.Description).HasMaxLength(200);    }  }}

RoleMap
namespace OADemo.DataModel.Domains.Maps{  public class PermissionMap:EntityMap<Permission>  {    public PermissionMap()    {      this.Property(o => o.Url).IsRequired();      this.Property(o => o.HttpMethod).IsRequired().HasMaxLength(30);    }  }}

PermissionMap

数据库上下文,也可以说是工作单元

using System.Data.Entity;using MTF.EFDatas;using System.Data.Entity.ModelConfiguration.Conventions;using OADemo.DataModel.Domains.Maps;namespace OADemo.DataModel.Domains{  public class OADemoDataContext:EFUnitOfWork  {    public OADemoDataContext() : base("OADemoDataContext") { }    public DbSet<UserEntity> User { get; set; }    public DbSet<RoleEntity> Role { get; set; }    public DbSet<Permission> Permission { get; set; }    public DbSet<UserRole> UserRole { get; set; }    public DbSet<UserPermission> UserPermission { get; set; }    public DbSet<RolePermission> RolePermission { get; set; }    protected override void OnModelCreating(DbModelBuilder modelBuilder)    {            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();      modelBuilder.Configurations.Add(new UserMap());      modelBuilder.Configurations.Add(new RoleMap());      modelBuilder.Configurations.Add(new UserRoleMap());      modelBuilder.Configurations.Add(new UserPermissionMap());      modelBuilder.Configurations.Add(new PermissionMap());      modelBuilder.Configurations.Add(new RolePermissionMap());      base.OnModelCreating(modelBuilder);    }  }}

OADemoDataContext

到此,EFCodefirst最基本的配置应该就是这样了,下面是服务层了同样引用DLL,DataModel层和安装相关的包,代码很少,就几个空类,主要是继承了父类

using MTF.EFDatas;using OADemo.DataModel.Domains;using System;namespace OADemo.BLL{  public class UserBll:BaseService<UserEntity,Guid,int>  {  }  public class RoleBll : BaseService<RoleEntity, Guid, int>  {  }  public class PermissionBll : BaseService<Permission, Guid, int>  {  }  public class UserRoleBll : BaseService<UserRole, Guid, int>  {  }  public class UserPermissionBll : BaseService<UserPermission, Guid, int>  {  }  public class RolePermissionBll : BaseService<RolePermission, Guid, int>  {  }}

EntityService

好了,准备就绪,下面到了MVC了,这里我还没真正开始呢,还只是相当于测试前面的工作,开玩,还是引用装包先。

在GlobalApplication_Start()方法,也就是程序开处,注入必要的依赖,贯穿整个生命周期

using System.Web.Mvc;using System.Web.Routing;using Autofac;using MTF.EFDatas;using OADemo.DataModel.Domains;using OADemo.BLL;using MTF.Domain;namespace OADemo.MVCApp{  public class MvcApplication : System.Web.HttpApplication  {    protected void Application_Start()    {      DependencyContainer.Initializer(o => {        o.RegisterType<OADemoDataContext>().As<EFUnitOfWork>().InstancePerLifetimeScope();        o.RegisterGeneric(typeof(EFRepository<,>)).As(typeof(IRepository<,>)).InstancePerLifetimeScope();        o.RegisterType<UserBll>();      });      AreaRegistration.RegisterAllAreas();      RouteConfig.RegisterRoutes(RouteTable.Routes);    }  }}

Application_Start()

还要配置AppConfig文件指定数据链接,这里就不展示了。

先搞个控制器,测试服务层,主要是看看分页部分

里面有注释,不多说了,这里用到了json.net,需要安装包,另外封装了一个表格html文本的帮助类,放在Common层在

using System.Linq;using System.Web.Mvc;using Newtonsoft.Json;using Newtonsoft.Json.Linq;using System.Text;using OADemo.Common;namespace OADemo.MVCApp.Controllers{  public class HomeController : Controller  {    //找到相关实体的服务,这里我之前插入500多条数据    private UserBll userService = DependencyContainer.Resolve<UserBll>();    //把分页类当全局属性用    private Pager<UserEntity,int> Pager { get { return userService.Pager; } }    // GET: Home    public ActionResult Index()    {      ViewBag.PageSize = Pager.PageSize;//初始值在Pager类中已经有默认的初始值      ViewBag.PageCount = Pager.PageCount;      return View();    }    /// <summary>    /// 展示表格,类名取的不地道,懒的改了    /// </summary>    /// <param name="pageIndex">当前选中的页码 应该也做成可空类型,懒的改</param>    /// <param name="pageSize">当前页的表格行数</param>    /// <returns></returns>    public ActionResult InitiaTable(int pageIndex,int? pageSize)    {      //配置可空类型,如果前台没有数据传来,给一个默认值      int size =(int)( pageSize == null ? 20 : pageSize);      //根据前台参数取得相应的数据      var users=Pager.GetPageList(pageIndex, size, o => o.Id).ToList();      //配置json格式的html文本,传给前台      var result = JsonStringHelper.GetTbodyJObjectStrin(users,        new string[] { "Name", "Age", "RealName", "Email" });      return Content(result.ToString());    }    /// <summary>    /// 设置每页展示的行数    /// </summary>    /// <param name="pageSize"></param>    /// <returns></returns>    public ActionResult SetPageSize(int? pageSize)    {      int size = (int)(pageSize==null?20:pageSize);      Pager.SetPageSize(size);      var jo = new JObject(new JProperty("pageCountJson", Pager.PageCount),new JProperty("pageSizeJson",size));      return Content(jo.ToString());    }  }}

控制器代码
using System.Collections.Generic;using System.Text;using Newtonsoft.Json.Linq;namespace OADemo.Common{  public class JsonStringHelper  {    /// <summary>    /// 通过反射将列表中指定的属性值装配成多行多列的Html文本    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="list"></param>    /// <param name="propertyNames">要输出的属性名</param>    /// <returns></returns>    public static string GetTbodyHtmlString<T>(List<T> list, params string[] propertyNames)    {      StringBuilder sb = new StringBuilder();            list.ForEach(o => {        sb.Append(@"<tr>");//做一个行的开始        List<string> values = new List<string>();//存放符合条件的值        var properties = o.GetType().GetProperties();//得到当前实体类型的所有属性        foreach (var property in properties)        {          if (propertyNames != null)          {            foreach (var name in propertyNames)            {              if (property.Name.Contains(name))                values.Add(property.GetValue(o).ToString());            }          }                  }        values.ForEach(v =>          {            sb.AppendFormat(@"<td>{0}</td>", v);//加入列          });        sb.Append(@"</tr>");//行结束      });      return sb.ToString();    }    /// <summary>    /// 通过反射将列表中指定的属性值装配成多行多列的json格式的Html文本    /// </summary>    /// <typeparam name="T"></typeparam>    /// <param name="list"></param>    /// <param name="propertyNames"></param>    /// <returns></returns>    public static JObject GetTbodyJObjectStrin<T>(List<T> list, params string[] propertyNames)    {      return new JObject(new JProperty("tbodyJson", GetTbodyHtmlString(list, propertyNames)));    }  }}

表格html帮助类代码

前台用的bootstrap,做了个基本的表格展示 数据,分页用的是jqPaginator插件,前端还不会封装,对JQ不是很熟,作了注释的

@{  Layout = null;}<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta http-equiv="X-UA-Compatible" content="IE=edge">  <meta name="viewport" content="width=device-width, initial-scale=1">  <link href="~/Content/bootstrap.min.css" rel="stylesheet" />  <script src="~/scripts/jquery-1.9.1.min.js"></script>  <script src="~/scripts/bootstrap.min.js"></script>  <script src="~/scripts/jqPaginator.min.js"></script>  <script type="text/javascript">    $(function () {      var pageCount= @ViewBag.PageCount;//总页数,初始值从后台获取(全局变量)      var nPageSize= @ViewBag.PageSize;//每页显示的行数,初始值从后台获取(全局变量)      //jqPaginator插件,初始化分页      $.jqPaginator('#pager', {        totalPages: pageCount,//总页数        visiblePages: 10,//分页条中显示的条数        currentPage: 1,//默认选中的页码        first: '<li ><a href="javascript:;">首页</a></li>',        prev: '<li ><a href="javascript:;">上一页</a></li>',        next: '<li ><a href="javascript:;">下一页</a></li>',        page: '<li ><a href="javascript:;">{{page}}</a></li>',        last: '<li ><a href="javascript:;">尾页</a></li>',        //换页事件,参数num为当前选中的页码,type指示是否是通过点击        onPageChange: function (num, type) {          $("#table tbody").empty();//先将tbody清空          var tbodyHtml = "";//变量:tbody的html代码          //Ajax异步发送数据到后台          $.post("/Home/InitiaTable/",            {              pageIndex: num,//              pageSize: "20"//默认为20条            },            //Ajax回调函数            InitiaTable          )        }      });      //btnSetPageSize点击事件      $("#btnSetPageSize").click(function(){        //还是Ajax的post与后台交互        $.post("/Home/SetPageSize/",//请求页面          {pageSize:$("#pageSize").val()},//传递参数          //回调函数          SetPageSize          );      });          });    //参数data为后台传来装配好的tbody的html文本    function InitiaTable (data) {      var json = eval("(" + data + ")");//转换为json格式      var tbodyHtml = json.tbodyJson;   //获取html                  $("#table tbody").append(tbodyHtml);//添加到tbody中    }    //参数data为后台传来的总页数    function SetPageSize(data){      nPageSize=$("#pageSize").val();//重新加载当前选中页的行数      var json=eval("(" + data + ")");//接收后台数据,转换成json格式      pageCount = json.pageCountJson;//加载总页数,以传递给jqPaginator插件      //重新加载jqPaginator插件(对JQ不熟,不会封装的痛苦,复制的……)      $.jqPaginator('#pager', {        totalPages: pageCount,        visiblePages: 10,        currentPage: 1,        first: '<li ><a href="javascript:;">首页</a></li>',        prev: '<li ><a href="javascript:;">上一页</a></li>',        next: '<li ><a href="javascript:;">下一页</a></li>',        page: '<li ><a href="javascript:;">{{page}}</a></li>',        last: '<li ><a href="javascript:;">尾页</a></li>',        onPageChange: function (num, type) {          $("#table tbody").empty();          var tbodyHtml = "";          $.post("/Home/InitiaTable/",            {              pageIndex: num,              pageSize: nPageSize            },            InitiaTable          )        }      });    }  </script>  <title>Index</title></head><body>  <div class="container text-center" >     <div class="row" >      <div class="col-md-12">        <table class="table" id="table">          <thead>            <tr>              <td>Name</td>              <td>Age</td>              <td>RealName</td>              <td>Email</td>            </tr>          </thead>          <tbody>                      </tbody>        </table>      </div>    </div>    <div class="row">      <div class="col-md-8">                 <ul id="pager" class="pagination">          <li>                     </li>         </ul>                             </div>      <div class="col-md-4 " style="margin-top:25px">        <label for="pageSize" >设置每页行数</label>        <input type="text" style="width:25px" id="pageSize" />        <button type="button" class="btn-info" id="btnSetPageSize">设置</button>       </div>          </div>      </div></body></html>

前端代码

看看效果,开始进入时,展示默认数据

 我这里一共有501条数据,现在默认的是每页20条,点尾页,看看一共有多少页

设置每页13行

点尾页看看总页数

到此,我认为测试圆满结束,全部合格。但是最后我才发现数据多了一行。应该是JsonStringHelper类中的这句if (property.Name.Contains(name))有问题,属性名里包含了相同的字符串???这是我发表了之后才发现,现在太晚了,把这个改成if (property.Name==name)应该是没有问题的了。但是对这种相对硬的编码我还是有点感觉不爽。先就这样吧,太晚了。明天还在做苦力活啊,我都不是做这行的,不知道为什么 要熬夜写这些