使用EntityFramework的烦恼

我有一个应用程序,是实现数据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(-1), 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 0;
    }

    /// <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 = 0;
        // 先删除之前生成的统计数据(如果有)
        Expression<Func<T, bool>> where = GetDeleteExpression();
        LogHelper.Write("持久化删除_开始");
        i = olapDal.DeleteByWhere(where);
        LogHelper.Write("持久化删除_结束");
        // 保存本次的统计数据
        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(3 * 1000);
        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

时间: 2024-10-08 08:14:13

使用EntityFramework的烦恼的相关文章

struts2与JasperReport整合应用中解决PDF中文不显示问题(让我烦恼了半天)

今天在struts2中以pdf导出JasperReport报表时,遇到了一个很奇怪的问题:在action中获取一些值并且将其放到map中,但是通过$F{name}取值时,有些值能显示,而有些值不能显示,有些值只能显示部分.刚开始还以为是action存放到map中的key和jsper中取到的是不一致的,检查了半天发现并没有问题.这个问题然我郁闷了半天,始终没找到问题所在,由于不知道问题出在哪了,在百度上搜索了半天也没找到解决的办法,无意间看到了一片文章解决了我的问题,下面整理了一下此问题的解决方案

NYOJ 237 游戏高手的烦恼 &amp;&amp; POJ3041-Asteroids ( 二分图的最大匹配 )

链接: NYOJ 237  游戏高手的烦恼:click here~~ POJ  3041 Asteroids           :click here~~ 题意: 两题一样,翻译不同而已. 有一位传说级游戏高手,在闲暇时间里玩起了一个小游戏,游戏中,一个n*n的方块形区域里有许多敌人,玩家可以使用炸弹炸掉某一行或者某一列的所有敌人.他是种玩什么游戏都想玩得很优秀的人,所以,他决定,使用尽可能少的炸弹炸掉所有的敌人. 现在给你一个游戏的状态,请你帮助他判断最少需要多少个炸弹才能炸掉所有的敌人吧.

矩阵经典题目七:Warcraft III 守望者的烦恼(矩阵加速递推)

https://www.vijos.org/p/1067 很容易推出递推式f[n] = f[n-1]+f[n-2]+......+f[n-k]. 构造矩阵的方法:构造一个k*k的矩阵,其中右上角的(k-1)*(k-1)的矩阵是单位矩阵,第k行的每个数分别对应f[n-1],f[n-2],,f[n-k]的系数.然后构造一个k*1的矩阵,它的第i行代表f[i],是经过直接递推得到的.设ans[][]是第一个矩阵的n-k次幂乘上第二个矩阵,f[n]就是ans[k][1]. 注意:用__int64 #in

陌陌的烦恼 倾力摘掉约X标签

继在北上广深蓉五大城市的地铁站.公交候车站以及出租车进行了广告投放后,近日,陌陌又在颇具代表性的传统媒体<法制晚报>上发布了整版广告."你默默变成狼,我陌陌变成替罪羊"."还把我当约X利器,请别在白费力气"等广告文案在微博上引发网友大量讨论,#帮陌陌洗白白#的话题得到逾千万人关注. 成也约炮 困也约炮 陌陌前期的快速崛起,"约炮神器"这个标签起到了不小作用,虽然陌陌官方从未承认过自己以此炒作,但用户已对此形成深刻印象.瞄准基于地理位置

EntityFramework 4使用存储过程分页

1 CREATE PROC usp_OrgPage_SQL 2 @pageIndex INT, 3 @pageSize INT, 4 @totalCount INT OUTPUT 5 AS 6 BEGIN 7 SET @totalCount = (SELECT COUNT(*) FROM dbo.Organization) 8 SELECT * FROM 9 ( 10 SELECT *,ROW_NUMBER() OVER(ORDER BY OrganizationID DESC)AS row F

bzoj2539 丘比特的烦恼、黑书P333 (最优二分图匹配)

丘比特的烦恼 题目描述 Description 随着社会的不断发展,人与人之间的感情越来越功利化.最近,爱神丘比特发现,爱情也已不再是完全纯洁的了.这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭.于是丘比特千里迢迢远赴中国,找到了掌管东方人爱情的神--月下老人,向他求教. 月下老人告诉丘比特,纯洁的爱情并不是不存在,而是他没有找到.在东方,人们讲究的是缘分.月下老人只要做一男一女两个泥人,在他们之间连上一条红线,那么它们所代表的人就会相爱--无论他们身处何地.而丘比特的爱

EntityFramework 简单入个门

任何一个和数据相关的系统里,数据持久化都是一个不容忽视的问题. 一直以来,Java 平台出了很多 NB 的 ORM 框架,Hibernate.MyBatis等等..NET 平台上,ORM 框架这一块一直没有一个能吊到让几乎所有开发人员改掉以拼写 SQL 语句访问数据库的习惯. 实际上,在 .NET 平台上,也层出不穷的出现了很多类似的玩意儿,比如Nhibernate.Ibatis,还有微软的亲儿子--坑爹的 LinqToSQL.虽然这么多框架,但是真的没见过 .NET 平台的 ORM 框架能像

EntityFramework Core 1.1有哪些新特性呢?

前言 在项目中用到EntityFramework Core都是现学现用,及时发现问题及时测试,私下利用休闲时间也会去学习其他未曾遇到过或者用过的特性,本节我们来讲讲在EntityFramework Core 1.1中出现了哪些新特性供我们使用. EntityFramework Core 1.1新特性探讨 DbSet.Find 在EF 6.x中也有此方法的实现,在EF Core 1.1中也同样对此方法进行了实现,为什么要拿出来讲呢,当然也有其道理,我们一起来看看.在         public 

EntityFramework Core Raw SQL

EntityFramework Core Raw SQL 基础查询(执行SQL和存储过程) 啥也不说了,拿起键盘就是干,如下:     public class HomeController : Controller     {        private IBlogRepository _blogRepository;        public HomeController(IBlogRepository blogRepository)         {             _blo