(6)—总结
最后更新于:2022-04-01 11:42:15
1、实体应该要简单,层次最好平面化,这样有利于实体在各种通信中穿越(比如Webservices,WCF,Remoting,WCF RIA等);
2、虽然实体应该平面化,但并不代表不能有继承层次,因为这种层次可以获得很多管理和架构的好处
但要注意两点:
1是尽量采用接口,而且接口的方法或属性主要目的是提供统一访问实体的标准接口
2是属性的代码不要复杂,不要对外依赖,而方法更应该如此,能在操作类或者其它辅助类完成的尽量不要安排在实体类完成。
在整个架构体系中,实体类和系统定义的枚举等常量一样,应该处于引用的最底层,也就是实体类库原则上不引用你自己
定义的其它类库(当然,如果你把系统的常量定义,枚举定义等系统规范性定义放在一个类库,实体类库还是可以引用)。
3、采用根据实体(包括配置文件)动态构造SQL的方式与数据库交互,虽然大量的减少了数据访问和转换类,但也失去了很多灵活性,
如果关系数据库处理更加专业,那还是留给关系数据库去处理。
4、无论什么框架,数据从界面到数据库,该干的事情一件不能少,只是方式不同而已
实体框架提供的数据库访问功能,大部分都可以利用AutoCode工具自动生成,至于缓存,很多时候还是需要你自己去维护一部分。
5、成本的减少不是靠一个框架能解决,如果选择不当,反而会增加成本。
包括实体框架的学习成本,跟随成本,一些问题不太好解决的时候,曲线救国的成本等。
6、虽然没必要从汇编开始做起,但在可选的范围内,尽量使用较为原始的方式处理会根据持久性。
这主要是针对实体框架的劣势而言。
7、造船与租船:对于个人选哪个都可以,但对于国家则需要鼓励造船而不是租船。对于企业,在中国目前的氛围下,不做讨论。
8、项目管理者根据需要造或租,但作为程序员,还是多知道点造的技术为好;
9、虽然做技术不一定赚钱,赚钱不一定要技术,但做程序员还是要关注点技术,因为这至少暂时是你工作的需要。
整体上来讲,如果是项目的规模不是很大,可预见的业务关系不是很复杂,而且公司也不打算走产品路线,那么采用成熟的实体框架
应该是优先的选择;如果是个人搞一些系统,那当然是“拿来主义”至上。
在选择实体框架产品的时候,就我个人的观点,最好不要用微软的东西,虽然强大,但夹带的私货太多。
后记:在一个几乎人人都想一夜暴富的国度,谈技术都有些不合时宜....但惩罚却要开始了...
用市场没有换来值钱的技术,却换来了一堆连厕纸都不如的国债...道指暴泄,杯具的却是我们这些屁民,
奈何?
(5)–给实体管理类增加一点特色
最后更新于:2022-04-01 11:42:12
~~~
/// <summary>
/// 扩展实体管理类
/// </summary>
public static class EntityMgmtExtension
{
public static IEnumerable<T> Select<T>(this EntityMgmt<T> mgt, Selector<T> Selector)
{
return mgt.GetEntities(Selector);
}
}
~~~
一个简单的扩展方法,实际上NetFramework中的Where,Select,OrderBy,Join都是利用这种方式实现的,而Linq技术的本质也是这样实现的,只不过它采用了
类似于sql的语法。这个方式只能由编译器才能完成。
这篇的目的其实是为了说明,很多技术看起来很酷,但实际上并没有什么很特别的东西,学习要尽量了解其本质,而不要为了它的一些语法糖而迷失自己。
很多技术,很多概念其实就是别人给我们的一些羊皮,但我们乐此不疲的陷入这种扯羊皮的而不知追求更本质的东西时,看起来很傻的人却在得意的看着我们。
微软就是这样的,它的很多东西,都有这样的影子。比如微软的补丁,有的确实是需要补,但有些补丁却是为了让我们乐于补而补。
这个世界不仅仅有技术,还有利用技术做武器的商术,政术.....
(4)–实体的管理
最后更新于:2022-04-01 11:42:10
实体的管理主要包括如下功能:
A) 实现实体的缓存;
B) 实现实体的新增,修改,删除,查询等功能;
C)刷新缓存与数据库同步
D) 将缓存语句与数据库同步
上面是一些基本的功能,下面是一个基本示例:
~~~
//实体状态,对于实体采用缓存方式时有用。
public enum EntityState
{
NoChanging,
Added,
Modified,
Deleted
}
//这个类是真正实体的容器,目的是为了附加一些信息,便于实体的管理。
public class EntityItem<T>
{
public T StorageItem{get;set;}
public T CurrentItem{get;set;}
public T OrigialItem{get;set;}
public EntityState State{get;set;}
public EntityItem()
{
}
}
//利用委托,将选择等工作交给用户自己决定。
public delegate bool Selector<T>(T M);
//实体管理类,需要提供数据库连接管理,实体加载,查询,新增,删除,修改,与数据库同步等功能
//另外也需要提供事务支持功能,支持事务时需要提供能传入连接和事务实例的构造功能。
//或者本身实现事务,自我回滚,并抛出异常,这样可以实现多个管理类之间的任意组合式事务处理。
public class EntityMgmt<T>
{
//实体集合
private List<EntityItem<T>> _entities;
private string _connString;
public EntityMgmt():this("")
{
}
public EntityMgmt(string Conn)
{
_entities = new List<EntityItem<T>>();
_connString = Conn;
if (_connString == "")
{
//LoadFromConfigurationFile
}
}
private void LoadEntities()
{
//构造查询SQL,加载实体,可参见与数据库交互部分.
}
//提供查询功能
public IEnumerable<T> GetEntities(Selector<T> Selector)
{
List<T> theRets = new List<T>();
if (_entities.Count <= 0)
{
LoadEntities();
}
for (int i = 0; i < _entities.Count; i++)
{
if (_entities[i].State!= EntityState.Deleted && Selector(_entities[i].CurrentItem) == true)
{
theRets.Add(_entities[i].CurrentItem);
}
}
return theRets.AsEnumerable();
}
//新增一个实体
public void Add(T Item)
{
EntityItem<T> theItem = new EntityItem<T>();
theItem.CurrentItem = Item;
theItem.OrigialItem = Item;
theItem.State = EntityState.Added;
_entities.Add(theItem);
}
//修改
public void Update(T Item, Selector<T> SelectOldItem)
{
EntityItem<T> theEntityItem = null;
for (int i = 0; i < _entities.Count; i++)
{
if (SelectOldItem(_entities[i].CurrentItem) == true)
{
theEntityItem = _entities[i];
break;
}
}
if (theEntityItem != null)
{
theEntityItem.CurrentItem = Item;
theEntityItem.State = EntityState.Modified;
}
}
//接受改变,并将变化同步到数据库.
public void AcceptChanges()
{
foreach (var item in _entities)
{
switch (item.State)
{
case EntityState.Added:
DbOperation.InsertModel<T>(item.CurrentItem);
break;
case EntityState.Deleted:
break;
case EntityState.Modified:
//这里可以利用item.StorageItem做并发检测
break;
}
}
}
}
public enum CacheType
{
AppLevel,
SessionLevel,
None
}
//用工厂模式实现对实体管理类的创建,这样的好处是可以在这里实现对实体管理类本身的管理:
//全局单例模式,Session级共享等.
//这个类也可以实现对某个实体管理工厂的单例模式
public static class EntityMgmtFactory
{
private static readonly Dictionary<Type, CacheType> _CacheTypes = new Dictionary<Type, CacheType>();
static EntityMgmtFactory()
{
//可以根据文件配置加载缓存方式.
}
public EntityMgmt<T> CreateEntityMgmt<T>(string conn)
{
//根据缓存类型来创建或获取实体管理类的实例。
//这里不再赘述。缓存级别可以利用Runtime的Session.
return new EntityMgmt<T>(conn);
}
}
~~~
================================
上面的示例只是一些基本的功能,在实际应用中需要考虑的会更为复杂,比如可以设置失效期,自动同步等。另外也需要提供直接执行SQL的功能等。
AEF的方式比较复杂,但基本原理差不多,它的ESQL确实比较强大,这是其它一般框架所不具备的,但所谓成也萧何败也萧何,提供如关系数据库般得功能,
结果把简单问题复杂化了,反而在缓存,复杂查询,自由SQL支持方面显得不足(对自由SQL的支持实际上是违背AEF框架的目标的)。
(3)–与数据库交互
最后更新于:2022-04-01 11:42:08
实体框架一个基本的功能就是要与数据库交互,与数据库交互当然可以利用现有的一些基本技术,比如dotnet的ADO.Net等。这里有几个要点:
1)要封装底层的数据库交互通道,这个可以利用现有的数据库访问技术,比如ADO.Net。
2)构造与数据库交互的SQL语句,比如新增,删除,修改,查询等.做得比较好的,需要支持关联查询等。当然能做到AEF中的ESQL那样,还是有点难度。
下面是一个实例,这里我只构造插入语句,并实现现数据库插入实体的功能示意(底层数据库访问封装这里不讲,这个有很多例子)。
为了简单起见,我们假设实体类的类名就是数据库里对应表的表名,实体的属性对应数据库表里的字段,下面就是代码:
~~~
//实体类。
public class AModel
{
public string Field1{get;set;}
public string Field2 { get; set; }
public int age { get; set; }
public string Name { get; set; }
}
/// <summary>
/// SQL语句执行,可以采用普通类,也可以采用抽象接口,便于不同数据库的访问.
/// </summary>
public static class DatabaseHelper
{
public static void ExecuteSQL(string strSQL, SqlParameter[] Params)
{
;//......................
}
}
/// <summary>
/// 实体数据库基本操作,这里只是模拟实体插入数据库的情形,其它的操作其实类似,更新的话可以采用关键字做条件,冲突检查字段也可包含在条件里面.
/// </summary>
public static class DbOperation
{
public static void InsertModel<T>(T Model)
{
//这里的sql语句及参数构造结果可以采取缓存方式,没必要每次都去构造,以减少性能损失。
Dictionary<string,string> thePP = new Dictionary<string,string>();
Dictionary<string, SqlDbType> thePT = new Dictionary<string, SqlDbType>();
string theSQL = SQLBuilderHelper.BuilderInsertSQL<T>(thePP,thePT);
List<SqlParameter> theParams = new List<SqlParameter>();
foreach (var item in thePP)
{
SqlParameter theP = new SqlParameter();
theP.SqlDbType = thePT[item.Key];
theP.ParameterName = item.Value;
theP.Value = GetPropertyValue(Model, item.Key);
theParams.Add(theP);
}
DatabaseHelper.ExecuteSQL(theSQL.ToString(), theParams.ToArray());
}
//根据属性名获取属性值,可以单独放在一个辅助类里。
private static object GetPropertyValue<T>(T Model, string PropertyName)
{
Type theType = Model.GetType();
PropertyInfo pi = theType.GetProperty(PropertyName);
return pi.GetValue(Model, null);
}
}
/// <summary>
/// SQL及参数构建器,这里构造sql语句的时候,**在这里,默认类名就是数据库表名,属性名就是数据库字段名。但在实际构造这种框架的时候,可以采用元属性或者配置文件来实现这个功能**。比如linqtosql,DbContext就采用元属性,AEF,Hibernate就是采用xml格式的配置文件,但这些东西都是为了更好更准确的构造sql语句.构造这种
///sql语 句 需要知道的包括表名,字段名,字段类型,语言类型与字段类型间的对照等。
/// </summary>
public class SQLBuilderHelper
{
public static string BuilderInsertSQL<T>(Dictionary<string, string> PropertyAndParams,Dictionary<string,SqlDbType> thePAndDBType)
{
Type theType = typeof(T);
PropertyInfo[] theProperties = theType.GetProperties();
StringBuilder theSQL1 = new StringBuilder();
StringBuilder theSQL2 = new StringBuilder();
PropertyAndParams.Clear();
//其实这里也可以利用元属性来觉得哪些需要属性需要插入,哪些属性只能读取之类的判断,以及更新模式(对数字类型是赋值更新还是增量更新)等。
foreach (var p in theProperties)
{
if (theSQL1.ToString() == "")
{
theSQL1.Append(p.Name.ToUpper());
theSQL2.Append("@" + p.Name.ToUpper());
}
else
{
theSQL1.Append(","+p.Name.ToUpper());
theSQL2.Append(",@" + p.Name.ToUpper());
}
PropertyAndParams.Add(p.Name, "@" + p.Name.ToUpper());
thePAndDBType.Add(p.Name, GetDbType(p.PropertyType));
}
return "INSERT INTO " + theType.Name.ToUpper() + "(" + theSQL1.ToString() + ") VALUES(" + theSQL2.ToString() + ")";
}
//C#类型与数据库类型的对照可以通过Map文件完成,也可以通过元属性完成,个人觉得元属性的利用比较好,虽然说元属性不是很灵活,
//但减少了构造的文件依赖, 在大型的项目中其实是有好处的,至少是便于管理的。
private static SqlDbType GetDbType(Type type)
{
if (type == typeof(int))
{
return SqlDbType.Int;
}
return SqlDbType.NVarChar;
}
}
~~~
有的时候我们将实体对象称之为概念模型,而对象在数据库里的存储结构称之为物理模型,在这两个模型之间存在着一种映射关系(概念名称与物理名称的映射,对象属性与字段名之间的映射,以及对象之间关系的映射),这其实就是所谓的ORM(对象关系模型)。上面的例子这种映射是利用命名规范来完成,实际上做的时候可以采用配置文件或者元属性来完成,对于大项目时,配置文件的管理本身也会成为一个问题,因此我觉得用元属性数据来进行会比较好。
上面的实现只是一个简单的示例,实际做的时候需考虑得东西会比较多,比如需要封装数据库差异(底层访问差异,sql语句方言),实体与数据库之间的对照(map),SQL语句及参数缓存问题(没必要每次都构造)等,但不管怎么说,基本的原理还是一样的,关键技术主要是反射机制的应用。
下一篇,将介绍实体管理的功能,比如缓存,加载,新增,删除等....
(2)–实体框架的基本功能
最后更新于:2022-04-01 11:42:06
一般的实体框架都包括以下一些功能:
### 1、与物理存储交互
原因很简单,数据不能总在内存里,最终还是要存起来,所以,实体框架的一个基本功能就是要解决与物理存储之间的交互问题。这里的物理存储主要包括文件和数据库系统,以文件作为实体的物理存储方式,虽然不是很普遍,但也有用武之地,一些很小的应用系统,表不多,数据之间的关系不是很复杂的情况下,也可以直接采用文件进行存储,没必要为这种很小的应用还附带上一个数据库。就是采用了数据库,有些配置信息也可以放在文件中。
以数据库作为实体的物理存储方式,是目前应用系统普遍采用的一种方式,因此一般的实体框架都需要支持与数据库进行交互的功能:
A)支持多种数据库,至少要支持几种主流的数据库系统(屏蔽数据库差异);
B)支持对数据库的基本操作(定义,新增,修改,删除,查询等)
C)支持数据库事务
D)支持缓存与数据库之间的数据同步(这条不是必须,但好的框架至少要预留对这种需求的扩展).
2、对实体的管理
对实体的管理以下一些功能是基本的:
A)对实体的新增,修改,删除和查询并将这些操作最终反映到数据库;
B)SQL语句支持(有些事情无法完全针对实体完成的时候,还是要预留这种功能)
C)事务支持(最终要反映到数据库事务上来,好的还会支持跨数据事务)
D)实体缓存服务(包括是否需要缓存,缓存机制,失效策略等),如果更进一步,还应该支持数据缓存加载条件设置(这种功能其实很简单,但目前的很多框架都没有这种功能)
E) 支持缓存还必须提供缓存与数据库的数据同步机制;
F)如果支持缓存,也必须提供并发冲突检测和处理机制;
有些功能还提供一些更强大的功能:
A)实体高级查询,比如关联查询,子查询等。例如ESQL跟SQL就很相似。
B)实体操作的批处理。
C)实体操作类的单例、多例模式支持
上面列的基本都是实体框架应该提供的一些基本功能,还有一些实体有效性检查之类的功能,可以集成在这里,也可以不集成在这里,但一些规则性的东西还是需要提供,比如以元属性方式提供校验规则等。
(1)–实体与操作类
最后更新于:2022-04-01 11:42:03
### 1、什么是实体?
在我们进行系统构造的目标业务领域里,有一些对象,主要依赖外界进行管理或者处理,这些对象主要处在被加工或者处理的地位,这样的对象我们称之为实体对象,而这类对象以数据为住,一般只具有属性(或者叫域),不包含或只包含少量的内生方法(主要是一些自我处理的方法,这些方法不会操作其它对象,不产生对其它外界对象的依赖,比如克隆,格式化等)。直白的讲,实体就是数据性对象,结构体和类都可以用来描述它。实体类是允许嵌套的,即实体类的属性也可以是另外的实体类型(本身也可)。与实体类相对应的就是操作类(业务类,数据处理类等)。
### 2、实体操作类
这里的操作类主要是指针对实体进行操作的类,这些操作类除了包含一些必要的用来指导操作方法的属性外,一般都不包含数据性属性。操作类包含的方法一般都是针对实体或者实体集合的。通常情况下,操作单个实体和操作实体集合的类会分开。操作单个实体的,比如格式显示,反射赋值,反射取值;而针对实体集合的则主要是新增,修改,读取,删除,查询,筛选,计数,统计等操作。实体操作类和业务处理类的区别就是实体操作类只针对实体类本身,不带业务逻辑色彩。从数据库的角度看,实体操作类就相当于数据库的sql.
### 3、实体为何要与操作分离
实体代表数据,操作代表处理数据的方法,这种将实体和实体操作分离是面向对象编程下一个比较明智的改变,一方面因为面向对象只是分析和设计的一个视角,并不代表世界的全部,传统的那种将数据和方法分离的模式(面向过程)还是有其优势的,特别是在大规模数据处理方面。其二,实体类与实体的操作方法,特别是外界施加给实体的方法,从逻辑上来讲也很难理解。我一直觉得,面向过程和面向对象并不是对立的,它们之间是对立和统一的,面向对象侧重于整体和全局性,面向过程则注重局部与实现。
实体类和操作的分离的好处:
1)便于存储、运输、共享和交换
一般情况下实体都是很简单的东西,因为不带有方法,而只有数据,因此便于存储,运输,共享和交互。有利于以XML格式进行处理。
2)便于操作复用
如果实体遵循一定的规则(大多时候用元属性来描述),很多对实体的处理操作都可以通用方法。比如实体的增加,删除,修改,查询等。
3)便于与文件系统和数据库系统的结合
我们知道文件系统和数据库系统存放的主要是数据,而对于这些数据的处理都是外界附加的。实体与操作的分离,实体就非常适合与结构话的文件数据和数据库数据进行
转换,便于在内存中实施对这些数据的处理。
4)以实体而不是单纯的记录来表示数据,可以很好的利用类的特性,来聚合数据,可以更好的从整体方面来对待这些对象,比如订单实体,可以将表单头和订单明细数据作为整体来考虑。
不好的地方:
1)因为实体只包含属性,而不提供或者只提供少量的方法,使得原来可以封装在实体对象内的方法,也必须由外部提供,而且实体必需全面暴露自己。从这个方面来看,站在面向对象的角度讲,破坏了对象的封装性。同时由于实体主要包含的是静态特征,多态,继承的作用不是很大(提供一定的接口属性实现还是可以的)。其实这算不上不好的地方,有些事情没必要进入“洁癖”状态。
看到这里,大家或许会发现,实体与操作类的分离,有点数据库的味道,实际上现在有些持久层框架在某种意义上来讲,就是一个简化的内存关系数据库,比较典型的就是LINQToSQL,ADONetEntity Framework。
........
下一篇,主要讲实体框架主要功能和一般架构,对于一些关键技术,会提供代码实现。
同时也欢迎大家指正。
对使用通用框架的一点看法.
最后更新于:2022-04-01 11:42:01
现在业界充斥着各种各样的框架,有持久层的,也有业务逻辑层的,在一些项目上应用这种框架,就短期成本而已,应该是一个很好的选择,但框架毕竟是框架,有利也必然有弊。框架的学习其实并不难,但我觉得作为程序员,应该不仅仅停留在使用这个层面,还应该更深入的了解一下这些框架的基本原理和机制,以丰富自己的知识体系,不要说有利于自己去架构,就是使用也有很大的好处。对于企业采用框架,从成本上来说,如果是项目型的,短期当然是有利的,这种区别其实也是造船和租船的区别,但如果企业要形成自己的核心产品,还是不要采用这些框架为好,因为框架不是基本的技术,更多的是一种应用。而且每种框架都有自己的优势和劣势,总会有更好的框架出现,你如何去跟随呢?而一旦遇到框架的瓶颈,就很难在原来框架上做修改,拐弯抹角的解决问题固然可以,但最种会使得系统变得很复杂,很难看,很难管理的。而且作为这些开源性的框架,往往都是面向大众,针对普遍应用的,而对于具体的企业而言,往往是取其几点,就必须拿其一面,何苦呢?从项目管理上来讲,开源框架也往往会成为一种扯羊皮的东西。有的东西到底是框架的问题还是其它程序的问题,很难扯得清。
总的来讲,我觉得在业务逻辑比较复杂的大型系统中,我觉得可控性和一体性是最重要的,而这些恰恰是使用这些开源框架的不利之处。
前言
最后更新于:2022-04-01 11:41:59
> 原文出处:[解剖实体框架](http://blog.csdn.net/column/details/frame.html)
作者:[田世权](http://blog.csdn.net/hawksoft)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 解剖实体框架
> 框架的学习其实并不难,但我觉得作为程序员,应该不仅仅停留在使用这个层面,还应该更深入的了解一下这些框架的基本原理和机制,以丰富自己的知识体系,不要说有利于自己去架构,就是使用也有很大的好处。