我有一个应用程序,是实现数据ETL同步的,即把数据从一个db里抽取出来,经过处理后,存储到另一个db里。 O/RM采用的是EF db First。

随着项目程序的开发,EF的不足越来越明显。

● 根据EDM生成的类,没有继承关系,影响程序设计实现

我是直接根据edmx文件生成的类, 每个数据表对应一个class。 问题来了,这些类各自独立,没有面向对象里的封装和继承。

我的设计:各个数据同步类,抽象出来了一个基类,封装了对外接口方法、日志记录、异常捕获、告警、还有一些共有的处理方法等功能。基类是一个泛型类,T是代表的是POCO,由于EF生成的POCO并没有继承相同的基类,无奈我的这个基类只好这样定义:

public abstract class BizOrderETLBase<T> where T : class
{
private DateTime _timeFrom;
private DateTime _timeTo;
protected DateTime _TimeFrom
{
get { return _timeFrom; }
}
protected DateTime _TimeTo
{
get { return _timeTo; }
} public BizOrderETLBase(DateTime timeFrom, DateTime timeTo)
{
_timeFrom = timeFrom;
_timeTo = timeTo;
} public BizOrderETLBase()
: this(DateTime.Today.AddDays(-), DateTime.Today)
{ } /// <summary>
/// 执行ETL:Extract-Transform-Load
/// </summary>
/// <returns></returns>
public int DoETL()
{
LogHelper.Write(">>>>>开始同步业务订单{0} {1}~{2}...", this.GetType().Name,_timeFrom,_timeTo);
try
{
// 数据抽取和转换
var list = ExtractAndTransform(); if (list != null && list.Any())
{
// 数据装载到OLAP库
int i = Load(list);
LogHelper.Write("持久化共影响{0}条记录", i);
return i;
}
}
catch (Exception ex)
{
LogHelper.Write("同步业务订单{0}出现异常:\r\n{1}", this.GetType().Name, ex);
}
return ;
} /// <summary>
/// ETL之ET (Extract 和 Transform)
/// </summary>
/// <returns></returns>
protected abstract List<T> ExtractAndTransform(); /// <summary>
/// ETL之L (Load)
/// </summary>
/// <param name="datOrderList"></param>
/// <returns></returns>
private int Load(List<T> datOrderList)
{
// 持久化数据
var olapDal = new EFDbContext<T>();
int i = ;
// 先删除之前生成的统计数据(如果有)
Expression<Func<T, bool>> where = GetDeleteExpression();olapDal.DeleteByWhere(where);// 保存本次的统计数据
return olapDal.Add(datOrderList.ToArray());
} /// <summary>
/// 得到删除数据的条件
/// </summary>
/// <returns></returns>
protected abstract Expression<Func<T, bool>> GetDeleteExpression();
}

Load方法的功能主要是对已处理的数据进行写库,写库之前先删除现有数据。 删除的条件和数据抽取的条件一样,都是按一个名为ModifiedTime的时间戳字段判断的。 因为T被声明为了class类型,只好加了一个得到删除数据的条件的抽象方法GetDeleteExpression,各个继承类重写这个方法,而方法里的代码几乎相同,造成代码重复:

protected override Expression<Func<BizDatOrders, bool>> GetDeleteExpression()
{
Expression<Func<BizDatOrders, bool>> where = p => p.OrderModifiedTime >= _TimeFrom && p.OrderModifiedTime <= _TimeTo;
return where;
}

●EF无法实现事务隔离

由于数据源db的IO大,我这个ETL程序在从数据源获取数据时,经常出现死锁而被当成牺牲品。为了改进,不得不引入事务隔离级别,实现read uncommitted, 用写sql的方式很容易实现,在各表名后加nolock就可以了。 而EF没有现成的事务隔离,我使用了分布式事务TransactionScope。 问题来了,在TransactionScope里有多个不同连接的DbContext时,出现异常:与基础事务管理器的通信失败。
代码如下:

public class NoLockHelper
{
public delegate int EFdelegate(); public int NoLockInvokeDB(EFdelegate d)
{
var transactionOptions = new System.Transactions.TransactionOptions();
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
using (var transactionScope = new TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
{
int i = d.Invoke();
transactionScope.Complete();
return i;
}
}
} public class BizClass
{ public void Test()
{
new NoLockHelper().NoLockInvokeDB(Target);
} public int Target()
{
TravelPPEntities _travelPPContext = new TravelPPEntities();
var _airOrderRepository = _travelPPContext.Set<T_Business_AirOrders>();
var list = _airOrderRepository.Count();
Thread.Sleep( * );
Console.WriteLine(list);
_travelPPContext.Dispose(); OLAPEntities db = new OLAPEntities();
var lll = db.Set<BaseAirport>().ToList(); // 执行这句会报异常 return list;
}
}

异常信息:

System.Data.EntityException: 基础提供程序在 Open 上失败。 ---> System.Transactions.TransactionManagerCommunicationException: 与基础事务管理器的通信失败。 ---> System.Runtime.InteropServices.COMException: 事务管理器不可用。 (异常来自 HRESULT:0x8004D01B)
在 System.Transactions.Oletx.IDtcProxyShimFactory.ConnectToProxy(String nodeName, Guid resourceManagerIdentifier, IntPtr managedIdentifier, Boolean& nodeNameMatches, UInt32& whereaboutsSize, CoTaskMemHandle& whereaboutsBuffer, IResourceManagerShim& resourceManagerShim)
在 System.Transactions.Oletx.DtcTransactionManager.Initialize()
--- 内部异常堆栈跟踪的结尾 ---
在 System.Transactions.Oletx.OletxTransactionManager.ProxyException(COMException comException)
在 System.Transactions.Oletx.DtcTransactionManager.Initialize()
在 System.Transactions.Oletx.DtcTransactionManager.get_ProxyShimFactory()
在 System.Transactions.TransactionInterop.GetOletxTransactionFromTransmitterPropigationToken(Byte[] propagationToken)
在 System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
在 System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
在 System.Transactions.EnlistableStates.Promote(InternalTransaction tx)
在 System.Transactions.Transaction.Promote()
在 System.Transactions.TransactionInterop.ConvertToOletxTransaction(Transaction transaction)
在 System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts)
在 System.Data.SqlClient.SqlInternalConnection.GetTransactionCookie(Transaction transaction, Byte[] whereAbouts)
在 System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
在 System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
在 System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
在 System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
在 System.Data.ProviderBase.DbConnectionPool.PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, Transaction transaction)
在 System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
在 System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
在 System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
在 System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
在 System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
在 System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
在 System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
在 System.Data.SqlClient.SqlConnection.Open()
在 System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
--- 内部异常堆栈跟踪的结尾 ---
在 System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
在 System.Data.EntityClient.EntityConnection.Open()
在 System.Data.Objects.ObjectContext.EnsureConnection()
在 System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
在 System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
在 System.Data.Entity.Internal.Linq.InternalQuery`1.GetEnumerator()
在 System.Data.Entity.Internal.Linq.InternalSet`1.GetEnumerator()
在 System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()
在 System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
在 EntOlap.ETL.EF.EFDbContext`1.GetList() 位置 d:\SourceProject\OLAP\trunk\EntOlap\EntOlap.ETL\EntOlap.ETL.EF\EFDbContext.cs:行号 347

....

● 根据EDM生成的类和属性没有注释

这一点的确有点不太方便,降低了程序的可读性。

我曾试图从网络上资料解决这个问题,但还是不彻底。

使用EntityFramework的烦恼的更多相关文章

  1. C#实现如何判断一个数组中是否有重复的元素 返回一个数组升序排列后的位置信息--C#程序举例 求生欲很强的数据库 别跟我谈EF抵抗并发,敢问你到底会不会用EntityFramework

    C#实现如何判断一个数组中是否有重复的元素   如何判断一个数组中是否有重复的元素 实现判断数组中是否包含有重复的元素方法 这里用C#代码给出实例 方法一:可以新建一个hashtable利用hasht ...

  2. EntityFramework Core Raw SQL

    前言 本节我们来讲讲EF Core中的原始查询,目前在项目中对于简单的查询直接通过EF就可以解决,但是涉及到多表查询时为了一步到位就采用了原始查询的方式进行.下面我们一起来看看. EntityFram ...

  3. 恋爱虽易,相处不易:当EntityFramework爱上AutoMapper

    剧情开始 为何相爱? 相处的问题? 女人的伟大? 剧情收尾? 有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易 ...

  4. 关于这段时间学习 EntityFramework的 一点感悟

    Ado.Net,用了N多年,Entity Framework也关注了很多年. 每当项目转型的时候,就花费大巴的时间,学习一番,潮流的东西. 这个Orm很多,这个EF很火,这么多年了,我还是不敢用,虽然 ...

  5. 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)

    前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...

  6. 一次修改闭源 Entity Provider 程序集以兼容新 EntityFramework 的过程

    读完本文你会知道,如何在没有源码的情况下,直接修改一个 DLL 以去除 DLL 上的强命名限制,并在该程序集上直接添加你的“友元程序集(一种特殊的 Attribute,将它应用在程序集上,使得程序集内 ...

  7. ABP文档 - EntityFramework 集成

    文档目录 本节内容: Nuget 包 DbContext 仓储 默认仓储 自定义仓储 特定的仓储基类 自定义仓储示例 仓储最佳实践 ABP可使用任何ORM框架,它已经内置了EntityFrame(以下 ...

  8. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  9. 神马玩意,EntityFramework Core 1.1又更新了?走,赶紧去围观

    前言 哦,不搞SQL了么,当然会继续,周末会继续更新,估计写完还得几十篇,但是我会坚持把SQL更新完毕,绝不会烂尾,后续很长一段时间没更新的话,不要想我,那说明我是学习新的技能去了,那就是学习英语,本 ...

随机推荐

  1. NSNotification --关于通知

    一. 通知(NSNotification)的发送(Post)和注册(add)都必须借助于通知中心(NSNotificationCenter) 发送通知: NSNotificationCenter *c ...

  2. C#装箱和拆箱

    1.装箱是将值类型转换为引用类型(或者转换为此值类型所实现的任何接口类型)的隐式转换,当 CLR 对值类型进行装箱时,会将该值包装到 System.Object 内部,再将后者存储在托管堆上. ; / ...

  3. SQLServer : 几个特殊函数(RAND,CHECKSUM, NEWID)

    这一篇我们来捋捋在T-SQL中可能会用到的几个特殊的函数 1. 随机数: RAND 返回从 0 到 1 之间的随机 float 值. 语法: RAND ( [ seed ] ) seed 提供种子值的 ...

  4. 关于git(分布式版本控制系统)的一些操作和命令

    很久没上博客写东西了,今天闲着无聊就不想敲代码,所以看了看有没有一些好玩的东西,这不让我发现了git这个源代码版本控制工具,话不多说,就让我来简单的分享一下它的一些操作和命令以及命令所执行的含义吧! ...

  5. Spring 笔记

    依赖注入(Dependency Injection DI) 通过依赖注入,对象的依赖关系将由系统中负责协调对象的第三方组件在创建对象的时候进行设定.(p6 spring之旅) 在创建类的时候需要依赖的 ...

  6. 根据用户IP获得所在城市

    运行以下代码即可<html> <head> <meta http-equiv="Content-Type" content="text/ht ...

  7. 卸载AppDomain动态调用DLL异步线程执行失败

    应用场景 动态调用DLL中的类,执行类的方法实现业务插件功能 使用Assembly 来实现 但是会出现逻辑线程数异常的问题 使用AppDomain 实现动态调用,并卸载. 发现问题某个插件中开启异步线 ...

  8. 关于mock server

    这篇技术博客是在知乎上看到的 知乎js大神张云龙写的 这里贴过来记录下,如果侵权 请告知将及时删除. --------------------------- 为了更好的分工合作,让前端能在不依赖后端环 ...

  9. angular中的 ng-change

    <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta http-equiv="C ...

  10. java小结

    1.UUID.randomUUID()是潜在线程安全的,SecureRandom.nextBytes()为synchronized