CodeFirst

  一直以来我们写应用的时候首先都是创建数据库
  终于在orm支持codefirst之后,我们可以先建模。
  通过模型去创建数据库,并且基于codefirst可以实现方便的
  实现数据库迁移的工作.使用codefirst有以下几个技巧,
  以EntityFramework为例,结合我这个设计做了以下改进

1.模型的识别

  建立一个基类命名Entity,里面只有一个long类型的id字段。
  所有需要映射到数据库的模型都继承自Entity,

public class Entity
{
public long Id { get; set; }
}

2.模型的映射

  选用fluntapi作为配置(可以保持模型类的整洁,并且和具体orm无关)
      新建一个配置基类继承自EntityTypeConfiguration,并且添加泛型约束,
      在构造函数中配置表名(和类名一致),和id作为主键,并且设置成由程序生成。
      如果是一般单表的话,配合System.ComponentModel.DataAnnotations下的特性
      即可完成数据库字段长度等等限制

public class BaseEntityTypeConfig<TEntity> : EntityTypeConfiguration<TEntity>, IEntityConfiguration where TEntity : class, Entity
{
public BaseEntityTypeConfig()
{
HasKey(item => item.Id);
Property(item => item.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); ToTable(typeof(TEntity).Name);
}
}

3.模型的生成

  重写DbContext的OnModelCreating方法,反射程序集所有需要映射的类型,
  然后,查找对应配置,如果没有则构造出配置基类,即可完成模型的创建,
  ef默认会在第一次访问的时候去创建或者校验模型,
  模型的配置缓存在全局静态数据中。如果对应模型类有

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//这里 获取所有已经存在的配置
var configs = MetaDataManager.Type.Find(item =>
{
if (item.BaseType == null || !item.BaseType.IsGenericType)
return false;
if (item.BaseType.GetGenericTypeDefinition() != typeof(BaseEntityTypeConfig<>))
return false;
var genericType =
item.BaseType.GetGenericArguments()
.FirstOrDefault(data => data.IsSubclassOf(typeof(Entity)));
if (genericType == null)
return false;
return _context.Types.Contains(genericType);
}).ToDictionary(item => item.BaseType.GetGenericArguments().FirstOrDefault(data => data.IsSubclassOf(typeof(Entity))), item => item); //如果对应的实体有配置则从配置生成,如果没有配置,那么默认给出配置
_context.Types.ForEach(item =>
{
if (IgnoreAttribute.IsDefined(item))
return;
Type type;
if (!configs.TryGetValue(item, out type))
type = typeof(BaseEntityTypeConfig<>).MakeGenericType(item);
dynamic config = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(config);
}); }

Repository

  关于Repository的文章很多,这里就不重复描述了。
  我这里都是采用的接口编程,全部是采用构造函数来注入。
  我这里需要为每个类型的CurdService注入一个默认Repository实现。

Assembly.GetExecutingAssembly().GetTypes().Where(item => item.IsSubclassOf(typeof (Entity)))
.ForEach(item =>
{
var interfaceType = typeof (IRepository<>).MakeGenericType(item);
var classType = typeof (Repository<>).MakeGenericType(item); UnityService.RegisterType(interfaceType, classType);
});

其他一些技术

UnitOfWork(工作单元)

  网上文章也很多,简单来说,把若干个数据库操作放在一起作为事务提交
  得益于ef的设计,ef使用dbcontext.savechanges()方法等价于unitofwork.commit()方法
  这部分的设计主要借鉴NLayerApp.
  如果没有ef我建议是把每一个增删改类型的sql命令做成委托,然后左后commit的时候
  使用事务提交。代码如下

List<Action<DbConnection>> works = new List<Action<DbConnection>>();

public void Excute(Action<DbConnection> work)
{
works.Add(work);
} public void Commint()
{
using (TransactionScope ts = new TransactionScope())
{
using (var conn = new SqlConnection("{链接字符串}"))
{
works.ForEach(work => work(conn));
}
ts.Complete();
}
}

Specification(规约)

  同样网上的文章也很多,我这里只是把他作为查询实体来使用.
  如果使用传统ado.net的方式,直接传表达式到Repository中的话
  将导致解析表达式特别复杂,而用Specification的话,相当于查询
  语句中的where部分由它来接管,这样解耦和Repository和具体orm的依赖
  这部分的设计主要借鉴NLayerApp

多排序

  在分页中我们经常遇到多查询的情况,核心就是构造表达式,等同于构造
  sql语句中orderby的部分,同时它也支持内存中的多排序
  这部分设计主要借鉴Apworks中多排序的设计

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Coralcode.Framework.Models; namespace Coralcode.Framework.Domains
{
public class SortExpression<TEntity> where TEntity : class
{
/// <summary>
/// key:属性名,value,true为升序,false为降序
/// </summary>
private readonly List<EditableKeyValuePair<Expression<Func<TEntity, dynamic>>, bool>> _sortList; public SortExpression(List<EditableKeyValuePair<Expression<Func<TEntity, dynamic>>, bool>> sortList)
{
_sortList = sortList;
} /// <summary>
/// 如果为空则不需要排序
/// </summary>
/// <returns></returns>
public bool IsNeedSort()
{
return _sortList.Count != 0;
} public IQueryable<TEntity> BuildSort(IQueryable<TEntity> query)
{
if (_sortList == null || _sortList.Count == 0)
return query;
_sortList.ForEach(item =>
{
//获取表达式变量参数 item
var parameter = item.Key.Parameters[0]; //解析属性名
Expression bodyExpression = null;
if (item.Key.Body is UnaryExpression)
{
UnaryExpression unaryExpression = item.Key.Body as UnaryExpression;
bodyExpression = unaryExpression.Operand;
}
else if (item.Key.Body is MemberExpression)
{
bodyExpression = item.Key.Body;
}
else
throw new ArgumentException(@"The body of the sort predicate expression should be either UnaryExpression or MemberExpression.", "sortPredicate");
MemberExpression memberExpression = (MemberExpression)bodyExpression;
string propertyName = memberExpression.Member.Name; //根据属性名获取属性
var property = typeof(TEntity).GetProperty(propertyName); //创建一个访问属性的表达式 item.property
var propertyAccess = Expression.MakeMemberAccess(parameter, property); //创建表达式 item=>item.property
var orderByExp = Expression.Lambda(propertyAccess, parameter); var resultExp = Expression.Call(typeof(Queryable),
item.Value ? "OrderBy":"OrderByDescending" ,
new[] { typeof(TEntity), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));
query = query.Provider.CreateQuery<TEntity>(resultExp);
});
return query;
} public IEnumerable<TEntity> BuildSort(IEnumerable<TEntity> query)
{
if (_sortList == null || _sortList.Count == 0)
return query;
_sortList.ForEach(item =>
{
//获取表达式变量参数 item
var parameter = item.Key.Parameters[0]; //解析属性名
Expression bodyExpression = null;
if (item.Key.Body is UnaryExpression)
{
UnaryExpression unaryExpression = item.Key.Body as UnaryExpression;
bodyExpression = unaryExpression.Operand;
}
else if (item.Key.Body is MemberExpression)
{
bodyExpression = item.Key.Body;
}
else
throw new ArgumentException(@"The body of the sort predicate expression should be either UnaryExpression or MemberExpression.", "sortPredicate");
MemberExpression memberExpression = (MemberExpression)bodyExpression;
string propertyName = memberExpression.Member.Name; //根据属性名获取属性
var property = typeof(TEntity).GetProperty(propertyName); //创建一个访问属性的表达式 item.property
var propertyAccess = Expression.MakeMemberAccess(parameter, property); //创建表达式 item=>item.property
var orderByExp = Expression.Lambda(propertyAccess, parameter); //var resultExp = Expression.Call(typeof(IEnumerable),
// item.Value ? "OrderBy" : "OrderByDescending",
// new[] { typeof(TEntity), property.PropertyType }, query.Expression, Expression.Quote(orderByExp));
// query =resultExp.Method.Invoke(query,resultExp.Arguments.ToArray()) query.Provider.CreateQuery<TEntity>(resultExp);
});
return query;
} }
}

Ps:

  比较零碎,如果有什么问题可以在下面给我留言。
  其中模型创建代码中有一个_context.这个属于下个系列内容。
      主要用来搭配模块化做业务垂直分库用.
  重点是看设计思路,代码只是给一个演示,一般照搬是编译不过的.
      文章系列的结尾会放出一个完整的设计代码和一个简单的示例.

CRUD全栈式编程架构之数据层的设计的更多相关文章

  1. CRUD全栈式编程架构之导入导出的设计

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  2. CRUD全栈式编程架构之更精简的设计

    精简的程度 ViewModel精简 服务精简 控制器精简 Index.cshmtl精简 AddOrEdit.cshtml精简 效果:最精简的情况下,只需要写Entity这一个数据库实体然后加上一些简单 ...

  3. CRUD全栈式编程架构之MVC的扩展设计

    MVC执行流程 路由的扩展 我理解的路由作用有以下几个 Seo优化,用“/”分开的url爬虫更爱吃 物理和逻辑文件分离,url不再按照文件路径映射 Controller,Action的选择 MVC路由 ...

  4. CRUD全栈式编程架构之界面层的设计

    Layout的设计 模板模式 mvc的模板特别类似设计模式中模板方法模式,结合Layout中RenderSection和RenderBody方法可以将部分html展现逻辑延迟到具体的视图页面去实现里面 ...

  5. CRUD全栈式编程架构之服务层的设计

    服务层代码 首先我先放出2个主要类的代码再分别讲解 接口 using System; using System.Collections.Generic; using System.Linq; usin ...

  6. CRUD全栈式编程架构之控制器的设计

    页面 这里界面我采用jquery miniui来做的,当你完全了解了整个设计之后可以轻松切换到其他的js框架,个人认为类似muniui,easyui等等这类可以将web界面做得和winform类似的框 ...

  7. CRUD全栈式编程架构总结

    这里放出实例代码 github.com/SkyvenXiong/HCC

  8. CRUD全栈式编程概述

    业务场景 CRUD,从数据驱动的角度几乎所有的的业务都是在做这样的事情.  几乎所有的操作都是在做对表的增删改查.  假设我们将数据库数据规个类:  分为基础/配置数据和业务/增长数据,或者说静态数据 ...

  9. 大数据全栈式开发语言 – Python

    前段时间,ThoughtWorks在深圳举办一次社区活动上,有一个演讲主题叫做“Fullstack JavaScript”,是关于用JavaScript进行前端.服务器端,甚至数据库(MongoDB) ...

随机推荐

  1. Mixamo Fuse10分钟创建角色

    http://edu.manew.com/course/132 太6了

  2. 《腾讯游戏人生》微信小程序开发总结

    为打通游戏人生擂台赛与线下商家的O2O衔接,同时响应时下日臻火热的微信小程序,项目团队决定也开发一款针对性的微信小程序,以此方便商家在我们平台入驻并进行擂台赛事的创建和奖励的核销,进一步推广擂台赛的玩 ...

  3. 操作系统管理CPU的直观想法

    CPU的工作原理 要想管理CPU,就要先学会如何使用CPU.我们先从一个程序的执行来看看CPU是如何工作的. void main(){ int i , sum; ; i < ; i++){ su ...

  4. 初学C#——选号器

    private void Form1_Load(object sender, EventArgs e) { Random x = new Random(); ); //生成一个大于等于0,小于100之 ...

  5. artDialog组件应用学习(四)

    一.在对话框自定义操作按钮 预览: html调用代码: var btnArray = [ { value: '同意', callback: function () { this.content('你同 ...

  6. 【学习笔记】实用类String的基本应用即常用方法

    一.String类概述 在Java中,字符串被作为String类型的对象来处理. String类位于java.lang包中,默认情况下会自动导入到所有的程序中. 创建String对象的方法如下: St ...

  7. 天气小雨, 心情多云, 练习标准的键盘ABC打法

    今天看到饿了么转型生活做千亿美元公司 突然想到一些就写下来 当时外卖一份8元 10元的年代那个开心啊 很久以前宁可跑个远, 都不愿意叫外卖 叫了大概1年的外卖了, 之前还感到便宜多样, 现在感觉到的是 ...

  8. Oracle JDBC 连接卡死后 Connection Reset

    坑 这绝对是我碰计算机以来遇到的第一大坑! 症状: 在Linux主机上远程登录,执行一个简单的Oracle的JDBC连接程序(jar包),结果硬生生的卡在了连接建立验证阶段,然后等上几分钟后因为连接超 ...

  9. 使用angular帮你实现拖拽

    拖拽有多种写法,在这里就看一看angular版的拖拽. <!DOCTYPE html> <html ng-app="myApp"> <head> ...

  10. css border-radius的用法及自适应的椭圆

    我们知道border-radius允许您为元素添加圆角边框! 而border-radius 属性是一个简写属性,用于设置四个 border-*-radius 属性. 如果省略 bottom-left, ...