SportsStore是《精通ASP.NET MVC3框架(第三版)》中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器、URL优化、导航、分页、购物车、订单、产品管理、图像上传......是不错的MVC实践项目,但该项目不是放在多层框架下开发的,离真实项目还有一段距离。本系列将尝试在多层框架下实现SportsStore项目,并用自己的方式实现一些功能。

本篇为系列第一篇,包括:

■ 1、搭建项目
■ 2、卸载Entity Framework组件,并安装最新版本
■ 3、使用EF Code First创建领域模型和EF上下文
■ 4、三层架构设计
    □ 4.1 创建DAL层
        ※ 4.1.1 MySportsStore.IDAL详解
        ※ 4.1.2 MySportsStore.DAL详解

1、搭建项目

MySportsStore.Model:类库,领域模型、Entity Framework上下文所在层
MySportsStore.IDAL:类库,数据接口层
MySportsStore.DAL:类库,数据层
MySportsStore.IBLL:类库,业务逻辑接口层
MySportsStore.BLL:类库,业务逻辑实现层
MySportsStore.Common:类库,帮助层,存放各种帮助类,比如加密帮助类、缓存帮助类、JSON序列化类等
MySportsStore.WebUI:MVC4项目,并设置为"启动项目"
MySportsStore.Tests:类库,测试层

2、卸载Entity Framework组件,并安装最新版本

由于是在MVC4.0下创建的MySportsStore.WebUI,默认的EF版本是4.0版本,而在其它层,比如MySportsStore.Mode层,也会用到EF,而通过NuGet下载到的是最新版本,这样很容易造成版本不一致。所以,先把MySportsStore.WebUI中的EF组件卸载掉,统一安装最新版本的EF。

打开:工具--程序包管理器--程序包管理器控制台,默认项目选择"MySportsStore.WebUI",在控制台输入如下命令:

Uninstall-Package EntityFramework –Force

再在MySportsStore.WebUI下右键"引用",选择"管理NuGet程序包",下载最新版本的EF。

3、使用EF Code First创建领域模型和EF上下文

MySportsStore.Model下右键"引用"添加程序集:System.ComponentModel.DataAnnotations

添加领域模型Product:

using System.ComponentModel.DataAnnotations;namespace MySportsStore.Model
{public class Product{[Key]public int Id { get; set; }[MaxLength(100)]public string Name { get; set; }[MaxLength(500)]public string Description { get; set; }public decimal Price { get; set; }[MaxLength(50)]public string Category { get; set; }}
}

下载最新版本的EF。安装完后,在MySportsStore.Model下会多出一个App.config文件。

创建EF上下文类:

using System.Data.Entity;namespace MySportsStore.Model
{public class EfDbContext : DbContext {public EfDbContext(): base("conn") {Database.SetInitializer(new EfDbInitializer());//Database.SetInitializer(new CreateDatabaseIfNotExists<EfDbContext>());//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EfDbContext>());//Database.SetInitializer(new DropCreateDatabaseAlways<EfDbContext>());
        }public DbSet<Product> Products { get; set; }}
}

创建数据库的种子数据:

using System.Collections.Generic;
using System.Data.Entity;namespace MySportsStore.Model
{public class EfDbInitializer : CreateDatabaseIfNotExists<EfDbContext>{protected override void Seed(EfDbContext context){IList<Product> defaultProducts = new List<Product>();defaultProducts.Add(new Product(){Name = "Kayak", Description = "A boat for one person", Category = "Watersports", Price = 275.00M});defaultProducts.Add(new Product() { Name = "Lifejacket", Description = "Protective and fashionable", Category = "Watersports", Price = 48.95M });defaultProducts.Add(new Product() { Name = "Soccer ball", Description = "FIFA-approved size and weight", Category = "Soccer", Price = 19.50M });defaultProducts.Add(new Product() { Name = "Corneer flags", Description = "Giving your playing field that professional touch", Category = "Soccer", Price = 34.95M });defaultProducts.Add(new Product() { Name = "Stadium", Description = "Flat-packed 35,000-seat stadium", Category = "Soccer", Price = 79500.00M });defaultProducts.Add(new Product() { Name = "Thinking cap", Description = "Improve your brain efficiency by 75%", Category = "Chess", Price = 16.00M });defaultProducts.Add(new Product() { Name = "Unsteady Chair", Description = "Secretly give your opponent a disadvantage", Category = "Chess", Price = 29.95M });defaultProducts.Add(new Product() { Name = "Human Chess", Description = "A fun game for the whole family", Category = "Chess", Price = 75.00M });defaultProducts.Add(new Product() { Name = "Bling-bling King", Description = "Gold-plated, diamond-studded King", Category = "Chess", Price = 1200.00M });foreach (Product p in defaultProducts){context.Products.Add(p);}base.Seed(context);}}
}

4、三层架构设计

→DAL层:数据库访问层,负责和数据库交互
● IBaseRepository:是所有IXXXRepository接口的基类,提供了各个IXXXRepository泛型基接口的实现,避免了各个IXXXRepository接口的代码重复
● IProductRepository:实现IBaseRepository接口
● BaseRepository:是所有XXXRepository的基类,提供各个XXXRepository的泛型基类实现,避免了各个XXXRepository的代码重复
● ProductRepository:实现IProductRepository接口,派生于BaseRepository

→DbSession层:数据库访问层的统一入口
● 从中可以拿到各个IXXXRepository接口类型
● 在这里保存EF的所有变化
● 在这里执行SQL语句

→BLL层:业务逻辑层,借助数据库访问层统一入口执行业务逻辑
● IBaseService:是所有IXXXService的基类,提供了各个IXXXService的泛型基接口的实现,避免了各个IXXXService接口的代码重复
● IProductService:实现IBaseService接口
● BaseService:是所有XXXService的基类,提供了各个XXXService的泛型基类实现,避免了各个XXXService的代码重复
● ProductService:实现IProductService接口,派生于BaseService

→UI层:控制器、视图、视图模型

→Domain Model领域模型:与数据库交互相关的模型

→Common:一些帮助类和帮助方法,比如加密、缓存、JSON序列化等

→DTO:负责把领域模型转换成视图模型,比如使用AutoMappeer自动映射

4.1 创建DAL层

4.1.1 MySportsStore.IDAL详解

→IBaseRepository接口

所有的数据接口层的方法基本上是一样的,包括查询、分页查询、添加、批量添加、更新、批量更新、删除、批量删除等。所以,有必要针对所有的数据接口层提炼出一个泛型数据接口基类:

using System;
using System.Linq;
using System.Linq.Expressions;namespace MySportsStore.IDAL
{public interface IBaseRepository<T> where T : class,new(){//查询IQueryable<T> LoadEntities(Expression<Func<T, bool>> whereLambda);//分页查询IQueryable<T> LoadPageEntities<S>(Expression<Func<T, bool>> whereLambad,Expression<Func<T, S>> orderBy,int pageSize,int pageIndex,out int totalCount,bool isASC);//查询总数量int Count(Expression<Func<T, bool>> predicate);//添加
        T AddEntity(T entity);//批量添加int AddEntities(params T[] entities);//删除int DeleteEntity(T entity);//批量删除int DeleteBy(Expression<Func<T, bool>> whereLambda);//更新
        T UpdateEntity(T entity);//批量更新int UpdateEntities(params T[] entities);}
}

查询返回类型为什么用IQueryable<T>,而不用IEnumerable<T>类型?
IQueryable接口实现IEnumerable接口,IQueryable接口拥有IEnumerable的所有功能。

两者的区别可以从以下例子看出端倪:

IEnumerable<T> result = (from t in context.Tableorder by t.Idselect c).AsEnumerable().Take(3);

如果返回的是IEnumerable<T>类型,当执行AsEnumerable()后,会把所有的数据加载到本地内存,然后取出前3条数据。

IQueryable<T> result = (from t in context.Tableorder by t.Idselect c).Take(3);

如果返回的是IQueryable<T>类型,只是在数据库端取出前3条数据。

在这里,为了减少带宽的消耗,选择返回IQuerayble接口类型,当然如果内存足够,需要更快的响应速度,也可以选择返回IEnumerable接口类型。

为什么选择Expression<Func<T, bool>>类型参数而不是Func<T, bool>?
从最终效果来讲,两者并没有区别,都是委托类型参数。两者的区别在于:Func<T, bool>是静态的多播委托,Expression<Func<T, bool>>中,Expression表达式树把静态委托看作是它的数据类型,在编译前使用Expression的静态方法把Func<T, bool>赋值给表达式树的各个属性,在运行时编译的时候,内部调用compile()方法把表达式树转换成静态委托Func<T, bool>。

简而言之,使用Expression<Func<T, bool>>有更强的灵活性,最终也会转换成委托类型。

→IProductRepository接口

所有的数据接口都用引用MySportsStore.Model的领域模型,所以需要引用MySportsStore.Model。

针对领域模型Product,其对应的仓储接口为:

using MySportsStore.Model;namespace MySportsStore.IDAL
{public interface IProductRepository : IBaseRepository<Product>{}
}

使用数据接口的基类接口的好处显而易见。

→IDbContextFactory接口,当前EF上下文的抽象工厂

在BaseRepository中会用到EF上下文的实例,我们借助"抽象工厂"生产DbContext的实例。

从NuGet安装最新版本的EF。

using System.Data.Entity;namespace MySportsStore.IDAL
{public interface IDbContextFactory{//获取当前上下文的唯一实例
        DbContext GetCurrentThreadInstance();}
}

4.1.2 MySportsStore.DAL详解

→添加引用

● 添加对最新版EF的引用
● 添加对MySportsStore.IDAL的引用
● 添加对MySportsStore.Model的引用

→DbContextFactory,实现抽象工厂IDbContextFactory接口,用来生产EF上下文实例

using System.Data.Entity;
using System.Runtime.Remoting.Messaging;
using MySportsStore.IDAL;
using MySportsStore.Model;namespace MySportsStore.DAL
{public class DbContextFactory : IDbContextFactory{//获取当前EF上下文的唯一实例public System.Data.Entity.DbContext GetCurrentThreadInstance(){DbContext obj = CallContext.GetData(typeof (EfDbContext).FullName) as DbContext;if (obj == null){obj = new EfDbContext();CallContext.SetData(typeof(EfDbContext).FullName, obj);}return obj;}}
}

通过CallContext线程槽可以获取到当前线程内的唯一EF上下文实例。

→BaseRepository,所有XXXRepository的泛型基类实现

using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using MySportsStore.IDAL;namespace MySportsStore.DAL
{public class BaseRepository<T> : IDisposable where T : class, new(){private DbContext db;public BaseRepository(){IDbContextFactory dbFactory = new DbContextFactory();db = dbFactory.GetCurrentThreadInstance();}//查询public virtual IQueryable<T> LoadEntities(Expression<Func<T, bool>> whereLambda){IQueryable<T> result = db.Set<T>().Where(whereLambda);return result;}//分页查询public virtual IQueryable<T> LoadPageEntities<S>(Expression<Func<T, bool>> whereLambada, Expression<Func<T, S>> orderBy,int pageSize,int pageIndex,out int totalCount,bool isASC){totalCount = db.Set<T>().Where(whereLambada).Count();IQueryable<T> entities = null;if (isASC){entities = db.Set<T>().Where(whereLambada).OrderBy(orderBy).Skip(pageSize*(pageIndex - 1)).Take(pageSize);}else{entities = db.Set<T>().Where(whereLambada).OrderByDescending(orderBy).Skip(pageSize*(pageIndex - 1)).Take(pageSize);}return entities;}//查询总数量public virtual int Count(Expression<Func<T, bool>> predicate){return db.Set<T>().Where(predicate).Count();}//添加public virtual T AddEntity(T entity){db.Set<T>().Add(entity);return entity;}//批量添加 每10条记录提交一次public virtual int AddEntities(params T[] entities){int result = 0;for (int i = 0; i < entities.Count(); i++){if(entities[i] == null) continue;db.Set<T>().Add(entities[i]);//每累计到10条记录就提交if (i != 0 && i%10 == 0){result += db.SaveChanges();}}//可能还有不到10条的记录if (entities.Count() > 0){result += db.SaveChanges();}return result;}//删除public virtual int DeleteEntity(T entity){db.Set<T>().Attach(entity);db.Entry(entity).State = EntityState.Deleted;return -1;}//批量删除public virtual int DeleteBy(Expression<Func<T, bool>> whereLambda){var entitiesToDelete = db.Set<T>().Where(whereLambda);foreach (var item in entitiesToDelete){db.Entry(item).State = EntityState.Deleted;}return -1;}//更新public virtual T UpdateEntity(T entity){if (entity != null){db.Set<T>().Attach(entity);db.Entry(entity).State = EntityState.Modified;}return entity;}//批量更新 每10条记录更新一次public virtual int UpdateEntities(params T[] entities){int result = 0;for (int i = 0; i < entities.Count(); i++){if(entities[i] == null) continue;db.Set<T>().Attach(entities[i]);db.Entry(entities[i]).State = EntityState.Modified;if (i != 0 && i%10 == 0){result += db.SaveChanges();}}//可能还存在不到10条的记录if (entities.Count() > 0){result += db.SaveChanges();}return result;}//释放EF上下文public void Dispose(){db.Dispose();}}
}

为什么BaseRepository没有实现IBaseRepository接口?
--的确,BaseRepository的绝大多数方法是IBaseRepository接口的实现,但BaseRepository是所有XXXRepository的基类泛型实现,它的存在是为了避免所有XXXRepository中重复代码。

为什么要实现IDisposable接口?
--的确,DbContext有默认的垃圾回收机制,但通过BaseRepository实现IDisposable接口,可以在不用EF上下文的时候手动回收,时效性更强。

→ProductRepository

using MySportsStore.IDAL;
using MySportsStore.Model;namespace MySportsStore.DAL
{public class ProductRepository : BaseRepository<Product>, IProductRepository{}
}

ProductRepository派生于BaseRepository<Product>完成方法的实现。
ProductRepository的行为受IProductRepository约束。

源码在这里。

“MVC项目实践,在三层架构下实现SportsStore”系列包括:

MVC项目实践,在三层架构下实现SportsStore,从类图看三层架构

MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等

MVC项目实践,在三层架构下实现SportsStore-02,DbSession层、BLL层

MVC项目实践,在三层架构下实现SportsStore-03,Ninject控制器工厂等

MVC项目实践,在三层架构下实现SportsStore-04,实现分页

MVC项目实践,在三层架构下实现SportsStore-05,实现导航

MVC项目实践,在三层架构下实现SportsStore-06,实现购物车

MVC项目实践,在三层架构下实现SportsStore-07,实现订单提交

MVC项目实践,在三层架构下实现SportsStore-08,部署到IIS服务器

MVC项目实践,在三层架构下实现SportsStore-09,ASP.NET MVC调用ASP.NET Web API的查询服务

MVC项目实践,在三层架构下实现SportsStore-10,连接字符串的加密和解密

MVC项目实践,在三层架构下实现SportsStore-11,使用Knockout实现增删改查

转载于:https://www.cnblogs.com/darrenji/p/3809219.html

MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等相关推荐

  1. MVC项目实践,在三层架构下实现SportsStore-03,Ninject控制器工厂等

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  2. MVC项目实践,在三层架构下实现SportsStore-06,实现购物车

    SportsStore是<精通ASP.NET MVC3框架(第三版)>中演示的MVC项目,在该项目中涵盖了MVC的众多方面,包括:使用DI容器.URL优化.导航.分页.购物车.订单.产品管 ...

  3. 什么是MVC?什么是三层架构?

    什么是MVC MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离的方法来组织代码. MVC主要作用是降低了视图与业务 ...

  4. CS通用项目系统搭建——三层架构第一天

    CS通用项目:使用三层架构进行搭建 三层架构: 表现层(UI(User Interface)):展示给用户的层面,包含窗体控件数据等信息. 业务逻辑层(BLL(Business Logic Layer ...

  5. java三层架构项目事例_三层架构实例

    一.概要 在我的上一篇博客中,我们谈了谈分层,到底为什么分层(http://blog.csdn.net/shan9liang/article/details/6836300) 这篇博客,准备用一个小D ...

  6. WPF MVVM 架构 Step By Step(2)(简单的三层架构示例及粘合代码GLUE code)

    WPF MVVM 架构 Step By Step(2)(简单的三层架构示例及粘合代码GLUE code) 原文:WPF MVVM 架构 Step By Step(2)(简单的三层架构示例及粘合代码GL ...

  7. Javaweb MVC设计模式、Modle发展史、项目分层和三层架构

    文章目录 MVC设计模式 MVC的目的 MVC举例 jsp+servlet+javabean模式 MVC的优点 MVC的缺点 Modle 发展史 项目分层 三层架构 MVC设计模式 MVC模式(Mod ...

  8. SSM框架实战详细教程(十四)贯穿项目实战之三层架构

    之前我们的项目足够简单,所以使用的是两层架构,现在为了学习Spring,需要使用行业中常见的三层架构,关于分层开发的原则请看下图:         本次对项目的调整,主要是由之前的controller ...

  9. 三层架构下实现用户登陆C#

    上篇文章讲到三层.接下来就通过一个实例详细的看怎么用三层实现用户登陆界面. 一.Model实体(LoginModel): namespace LoginModel {//加入类:UserInfo Mo ...

最新文章

  1. 用什么软件可以开在线会议啊?
  2. SpringMVC---web.xml的配置
  3. 0x0F19B7EC (ucrtbased.dll)处(位于 ex6.exe 中)引发的异常: 0xC0000005: 写入位置 0x00740000 时发生访问冲突。...
  4. 产品管理|产品设计流程[完整版]
  5. .net core mvc部署到IIS导出Word 提示80070005拒绝访问
  6. Docker源码分析(十):Docker镜像下载
  7. android后台获取view,android – 如何获取当前显示在AdapterView中的项目?
  8. 事故通报绝不能一报了事22344
  9. 在GoogPlay上发布的包Facebook登录失败提示签名问题
  10. Python 可变数据类型和不可变数据类型 - Python零基础入门教程
  11. spark写出分布式的训练算法_Spark0.9分布式运行MLlib的线性回归算法
  12. 如何创建SQL Server日志传送
  13. 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
  14. 东财在线计算机应用基础作业,《计算机应用基础》东财在线20秋第一套作业答案...
  15. Webservice 安全性访问
  16. 计算机2.0培训心得,2020信息技术2.0培训心得
  17. 自己碰到的一个“无法读取源文件或磁盘”问题处理
  18. 39岁java程序员,失业中,看不到希望,很迷茫,路在何方?
  19. 最新IOS开发学习资料整理(进阶必备)
  20. 从伊朗虚假新闻活动案例来看国外造谣及传谣模式

热门文章

  1. 申报高新技术企业的好处
  2. Day 3 (云计算-zsn)
  3. win10 who owns this pc? My organization I own it
  4. Cisco Packet Tracer 配置交换机与路由器静态路由
  5. uniapp对接微信公众号H5微信支付、分享、小程序隐藏右上角分享胶囊
  6. win10专业版 1909 netkeeper错误代码137
  7. Codevs 侦探推理
  8. DeepMind AI 科学家:2020年NLP和ML领域十大研究进展
  9. 网页抓取软件Wget用法详解
  10. 企业资产盘点系统能做什么