我有一个应用程序,是实现数据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生成的类和属性没有注释

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

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

最新文章

  1. SharePoint Online 创建门户网站系列之图片滚动
  2. vs win32 &amp; MFC 指针默认位置
  3. GC overhead limit exceeded填坑心得
  4. JM8.6学习
  5. Linux基础入门(20135207 王国伊)
  6. Windows phone 8 学习笔记(7) 设备(转)
  7. hadoop日志太大
  8. 【高德地图开发4】---增加覆盖物setMapTextZIndex
  9. SPRING IN ACTION 第4版笔记-第九章Securing web applications-003-把用户数据存在数据库
  10. oc中的类学习笔记1
  11. Linux多任务编程——进程
  12. 第三方系统打开EAFC的实现
  13. 公钥password学中的素数以及对称加密
  14. 某大神C#框架后台发送信息的查找及破解
  15. 【SDOI2009】HH的项链 (莫队)
  16. 套接字API
  17. VM 安装 linux Enterprise_R5_U4_Server_I386_DVD教程图解
  18. POJ 3304 Segments (叉乘判断线段相交)
  19. python使用websocket简单组建聊天室
  20. CODE FESTIVAL 2017 qual B 题解

热门文章

  1. (python) 标准模块sys和os的使用
  2. (python)对象的引用
  3. 通过外部配置文件做mybatis的基础配置,以及Mapper代理接口的实现
  4. 使用my exclipse对数据库进行操作(4)
  5. ActionScript 3.0 自写类整理笔记(十三)——Random类
  6. python 装饰器修改调整函数参数
  7. 【记忆化搜索】bzoj1652 [Usaco2006 Feb]Treats for the Cows
  8. 老毛桃u盘装系统制作工具
  9. wince mobile环境下播放WAV声音
  10. 64位系统 IIS中应用程序池设置导致 访问数据库错误