【问题标题】:Optimize Entity Framework 6 and reducing round trips优化 Entity Framework 6 并减少往返
【发布时间】:2014-09-03 00:05:12
【问题描述】:

我已经使用 EF6 大约一年了,我真的很喜欢它,但是作为一个有经验的 SQL Server 程序员,获得最佳性能是非常有趣的。我在网上搜索了很多关于优化和最小化往返的信息,但我没有找到像这样的问题的简单答案。我们针对我们的 SQL Server 2014 数据库使用仅围绕 EF 6 构建的数据层。我们正在接受 EF6,但我对目前的表现并不满意。我有更多涉及的方法,但让我问一个更简单的方法,例如下面的示例。

在下面的方法中,我想这是往返和查询 5 次?我知道我可以创建一个带有输出参数和往返一次的存储过程,也许这是解决这个问题的正确方法?如何优化此方法,以便以最有效和最快的方式返回结果?

    public static DataInspection GetDataInspection(int userId)
    {
        using (var context = new DemoEntities())
        {
            var di = new DataInspection();

            di.TripCount = context.Trips.Count(x => x.userId == userId);
            di.DayCount = context.Days.Count(x => x.userId == userId);
            di.LegCount = context.Legs.Count(x => x.userId == userId);
            di.MinTripSequenceStart = context.Trips.Where(x => x.userId == userId).Min(y => y.seqStartTime);
            di.MaxTripSequenceEnd = context.Trips.Where(x => x.userId == userId).Max(y => y.seqEndTime);

            return di;
        }
    }

这是为用户返回数据的第二种方法。我还想优化此方法以最大程度地减少往返行程并以最快的方式执行此方法。这些实体之间没有关系。

    public static UserData GetMetaData(int userId)
    {
        using (var context = new DemoEntities())
        {
            var cloudData = new DemoData();

            var aircraft = context.Aircraft.Where(x => x.userId == userId);
            var aircraftTypes = context.AircraftTypes.Where(x => x.userId == userId);
            var airlines = context.Airlines.Where(x => x.userId == userId);
            var airports = context.Airports.Where(x => x.userId == userId);
            var approaches = context.Approaches.Where(x => x.userId == userId);
            var approachesLegs = context.ApproachesLegs.Where(x => x.userId == userId);
            var binaries = context.BinaryCatalogs.Where(x => x.userId == userId);
            var crews = context.Crews.Where(x => x.userId == userId);
            var employmentEvents = context.EmploymentEvents.Where(x => x.userId == userId);
            var events = context.Events.Where(x => x.userId == userId);
            var hotels = context.Hotels.Where(x => x.userId == userId);
            var notes = context.Notes.Where(x => x.userId == userId);
            var payperiodevents = context.PayperiodEvents.Where(x => x.userId == userId);
            var payrollCategories = context.PayrollCategories.Where(x => x.userId == userId);
            var performance = context.Performances.Where(x => x.userId == userId);
            var positions = context.Positions.Where(x => x.userId == userId);
            var regOpsTypes = context.RegOperationTypes.Where(x => x.userId == userId);
            var usersettings = context.UserSettings.Where(x => x.userId == userId);
            var globalSettings = from p in context.GlobalSettings select p;

            cloudData.Aircraft = AircraftAssembler.ToDTOs(aircraft);
            cloudData.AircraftTypes = AircraftTypeAssembler.ToDTOs(aircraftTypes);
            cloudData.Airlines = AirlineAssembler.ToDTOs(airlines);
            cloudData.Airports = AirportAssembler.ToDTOs(airports);
            cloudData.Approaches = ApproachAssembler.ToDTOs(approaches);
            cloudData.ApproachesLegs = ApproachesLegAssembler.ToDTOs(approachesLegs);
            cloudData.Binaries = BinaryCatalogAssembler.ToDTOs(binaries);
            cloudData.Crews = CrewAssembler.ToDTOs(crews);
            cloudData.EmploymentEvents = EmploymentEventAssembler.ToDTOs(employmentEvents);
            cloudData.Events = EventAssembler.ToDTOs(events);
            cloudData.Hotels = HotelAssembler.ToDTOs(hotels);
            cloudData.Notes = NoteAssembler.ToDTOs(notes);
            cloudData.PayperiodEvents = PayperiodEventAssembler.ToDTOs(payperiodevents);
            cloudData.PayrollCategories = PayrollCategoryAssembler.ToDTOs(payrollCategories);
            cloudData.Performances = PerformanceAssembler.ToDTOs(performance);
            cloudData.Positions = PositionAssembler.ToDTOs(positions);
            cloudData.RegOperationTypes = RegOperationTypeAssembler.ToDTOs(regOpsTypes);
            cloudData.UserSettings = UserSettingAssembler.ToDTOs(usersettings);
            cloudData.GlobalSettings = GlobalSettingAssembler.ToDTOs(globalSettings);

            return cloudData;
        }
    }

如果有任何关于使用 VS 2013 C# 从 Entity Framework 6 中获得绝对最佳性能的提示,我将不胜感激。我已经看到公司使用 NHibernate 等产品并且存在严重的性能问题,现在我正在以更大规模的方式使用 EF 6,我也看到了这一点,我认为这不是产品的结果,而是实施,这就是我在这里寻求帮助的原因。

【问题讨论】:

  • 首先,确保往返确实是导致您的问题的原因。可能是一个糟糕的查询或数据库,或者 Web 应用程序中的 CPU 工作。查看实体框架未来的查询。我相信它仍然不是内置的,但可以用螺栓固定。
  • 另外,这是数据建模错误的结果。地球上(和天空中)飞机与 userId 有什么关系?有了适当规范化的数据,您只需几个有效的查询(可能是一个)就可以将这些数据作为对象图获取。
  • @GertArnold 我们有一个支持无限用户的数据库,每个用户由一个 userId PK INT 表示。在此示例中,我正在为特定用户提取数据。你建议怎么做?
  • “你建议怎么做”。改变数据模型后是怎样的。拥有一切的用户是不可能的。航空公司有飞机。用户没有。飞机有机组人员,用户没有,等等。如果您不能(或不会)更改模型,那么您的方式是唯一的方法,可能会按照 usr 的建议调整为查询包(未来的查询)。
  • @GertArnold 我认为您必须了解我们的应用程序才能理解这种设计。感谢您的反馈,但模型是它需要的。

标签: c# performance entity-framework tsql optimization


【解决方案1】:

您可以通过以下方式在一次往返中获取数据:

var data = (from trip in context.Trips.Take(1)
            let TripCount = trip.Count(x => x.userId == userId)
            let DayCount = context.Days.Count(x => x.userId == userId)
            let LegCount = context.Legs.Count(x => x.userId == userId)
            let MinTripSequenceStart = trip.Where(x => x.userId == userId).Min(y => y.seqStartTime)
            let MaxTripSequenceEnd = trip.Where(x => x.userId == userId).Max(y => y.seqEndTime)
            select new {
                TripCount,
                DayCount,
                LegCount,
                MinTripSequenceStart,
                MaxTripSequenceEnd
            }).ToList();

最后使用自动映射器将数据复制到DataInspection 类:

var dataInspection = data
            .Select(inspection => AutoMapper.Mapper.DynamicMap(inspection, inspection.GetType(), typeof(DataInspection)))
            .Select(di => di as DataInspection);

【讨论】:

  • 这对Trips 中的每个 记录执行计数。你可以添加Take(1),但这让它看起来更像是一个黑客。
  • @mo.esmp 感谢您提供代码提示,当然这是一种有趣的方法。我认为如果该用户没有 Trips,这将失败,即针对新用户场景的空数据库查询。可能必须改用 FirstOrDefault() 吗?
【解决方案2】:

在使用 SQL Server 2014 Profiler 观察往返行程以及连接重置以及研究各种有关优化的文章后,我得出了以下结论。

1) 将 AsNoTracking() 用于只读方法,例如:

var aircraft = context.Aircraft.AsNoTracking().Where(x => x.userId == userId);

2) 查询往返的地方,手动打开连接,通过调用代码保持打开状态,如下所示:

    using (var context = new DemoEntities())
    {
        var cloudData = new DemoData();
        context.Database.Connection.Open();

        var aircraft = context.Aircraft.Where(x => x.userId == userId);
        var aircraftTypes = context.AircraftTypes.Where(x => x.userId == userId);
        var airlines = context.Airlines.Where(x => x.userId == userId);
        //....rest of code here
    }

3) 对于 DataInspection 方法,我可以使用打开的连接,然后进行 5 次往返,但我选择使用带有输出参数的存储过程来访问数据库并根据我们的需要处理空值.

【讨论】:

    猜你喜欢
    • 2014-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 1970-01-01
    • 2014-01-09
    • 2012-07-20
    • 2013-02-12
    相关资源
    最近更新 更多