2016-06-28 5 views
3

Я новичок в структуре сущностей и люблю простоту, но у меня есть проблемы со скоростью. Я думаю, что я мог бы использовать ленивую загрузку неправильно, но с трудом обнимал ее вокруг себя. Я разделил слой модели данных и уровень бизнес-сущности и использовал функцию для создания бизнес-объекта из моей модели данных. В этой функции я перебираю различные вложенные объекты для создания соответствующих моделей. Хорошо, достаточно хаотичный здесь некоторый код:Запрос структуры Entity Framework слишком медленный

IM_ITEM.cs (модель данных продукта)

public partial class IM_ITEM 
{ 
    public IM_ITEM() 
    { 
     this.IM_INV = new HashSet<IM_INV>(); 
     this.IM_BARCOD = new HashSet<IM_BARCOD>(); 
     this.IM_GRID_DIM_1 = new HashSet<IM_GRID_DIM_1>(); 
     this.IM_GRID_DIM_2 = new HashSet<IM_GRID_DIM_2>(); 
     this.IM_GRID_DIM_3 = new HashSet<IM_GRID_DIM_3>(); 
     this.IM_PRC = new HashSet<IM_PRC>(); 
    } 

    public string ITEM_NO { get; set; } 
    public string DESCR { get; set; } 
    // many more properties... 

    public virtual ICollection<IM_INV> IM_INV { get; set; } 
    public virtual ICollection<IM_BARCOD> IM_BARCOD { get; set; } 
    public virtual ICollection<IM_GRID_DIM_1> IM_GRID_DIM_1 { get; set; } 
    public virtual ICollection<IM_GRID_DIM_2> IM_GRID_DIM_2 { get; set; } 
    public virtual ICollection<IM_GRID_DIM_3> IM_GRID_DIM_3 { get; set; } 
    public virtual ICollection<IM_PRC> IM_PRC { get; set; } 
} 

метод создания бизнес-объект:

public static ProductEntity FromEfObject(IM_ITEM obj) { 
    var product = new ProductEntity { 
     ItemNumber = obj.ITEM_NO, 
     StyleNumber = obj.VEND_ITEM_NO, 
     Title = obj.DESCR_UPR, 
     LongName = obj.ADDL_DESCR_1, 
     ShortDescription = obj.DESCR, 
     VendorCode = obj.ITEM_VEND_NO, 
     Quarter = obj.ATTR_COD_2, 
     Color = obj.PROF_ALPHA_2, 
     Markdown = obj.PRC_1, 
     Price = obj.REG_PRC ?? 0, 
     Status = obj.STAT, 
     DepartmentCode = obj.ATTR_COD_1, 
     DepartmentDigit = obj.ATTR_COD_1.Substring(0, 1), 
     MixAndMatch = obj.MIX_MATCH_COD, 
     Inventory = new Inventory(obj.IM_INV), 
     Sizes = new List<ProductSize>(), 
     Widths = new List<ProductSize>(), 
     Lengths = new List<ProductSize>(), 
     Barcodes = new Dictionary<string, string>() 
    }; 


    if (obj.IM_PRC.Any()) { 
     var price = obj.IM_PRC.First(); 
     product.DnsPrice2 = price.PRC_2.GetValueOrDefault(); 
     product.DnsPrice3 = price.PRC_3.GetValueOrDefault(); 
    } 

    foreach (var barcode in obj.IM_BARCOD) { 
     product.Barcodes.Add(barcode.DIM_1_UPR, barcode.BARCOD); 
    } 

    foreach (var size in obj.IM_GRID_DIM_1) { 
     product.Sizes.Add(ProductSize.FromEfObject(size)); 
    } 

    foreach (var width in obj.IM_GRID_DIM_2) { 
     product.Widths.Add(ProductSize.FromEfObject(width)); 
    } 

    foreach (var length in obj.IM_GRID_DIM_3) { 
     product.Lengths.Add(ProductSize.FromEfObject(length)); 
    } 

    if (!product.Sizes.Any()) { 
     product.Sizes.Add(new ProductSize()); 
    } 

    if (!product.Widths.Any()) { 
     product.Widths.Add(new ProductSize()); 
    } 

    if (!product.Lengths.Any()) { 
     product.Lengths.Add(new ProductSize()); 
    } 

    return product; 
} 

И мой метод, чтобы получить модель:

public ProductEntity GetProductById(string itemNumber, int storeNumber) { 
    var product = _unitOfWork 
     .GetProductRepository(storeNumber) 
     .GetQueryable() 
     .FirstOrDefault(p => p.ITEM_NO == itemNumber); 

    return product == null ? null : ProductEntity.FromEfObject(product); 
} 

И метод GetQueryable:

internal DbSet<TEntity> DbSet; 
public GenericRepository(TContext context) 
{ 
    Context = context; 
    DbSet = context.Set<TEntity>(); 
} 

public virtual IQueryable<TEntity> GetQueryable() 
{ 
    IQueryable<TEntity> query = DbSet; 
    return query; 
} 

Немного больше информации .. Я использовал первое моделирование базы данных для создания моей модели данных, а база данных, на которую я тестирую, не содержит тонны данных. Кроме того, я попытался использовать .Include() в моем методе GetProductById для загрузки (с нетерпением я верю), но замедлил его еще дальше.

Я делаю что-то принципиально неправильно? Или используется EF, чтобы быть медленным для такого запроса.

EDIT: Для того, чтобы предотвратить отложенную загрузку Я обновил свой запрос:

public ProductEntity GetProductById(string itemNumber, int storeNumber) { 
     var product = _unitOfWork 
      .GetProductRepository(storeNumber) 
      .GetQueryable() 
      .Include(p => p.IM_INV.Select(i => i.IM_INV_CELL)) 
      .Include(p => p.IM_BARCOD) 
      .Include(p => p.IM_GRID_DIM_1) 
      .Include(p => p.IM_GRID_DIM_2) 
      .Include(p => p.IM_GRID_DIM_3) 
      .Include(p => p.IM_PRC) 
      .FirstOrDefault(p => p.ITEM_NO == itemNumber); 

     return product == null ? null : ProductEntity.FromEfObject(product); 
    } 

При трассировке, это дает мне только один большой неприятный запрос, который занимает больше времени, чем при использовании отложенной загрузки http://pastebin.com/LT1vTETb

+3

Какова ваша реализация '_unitOfWork.GetProductRepository (storeNumber) .GetQueryable()', потому что если эта часть не так, это может быть, что EF запрашивает всю таблицу продукта в памяти перед выполнением «FirstOrDefault» и тем самым замедлить ее. – SynerCoder

+0

Обновлено с помощью этого кода .. спасибо! –

+0

Является ли Sql Server вашим репозиторием на заднем конце? Если это так, используйте Sql Server Profiler, чтобы узнать, что на самом деле выполняется на сервере. Если имеется несколько операторов (например, предложенный @SynerCoder), то это может быть часть кода, который вы не показываете нам, который будет реализовывать всю таблицу перед выполнением FirstOrDefault. В любом случае используйте это, чтобы увидеть, что выполняется. Если это выглядит правильно, начните анализ (настройку) запроса, если он выглядит неправильно, начните копать через код. – Igor

ответ

1

Вы можете оптимизировать ваш запрос, чтобы избежать ленивой загрузки. В этом случае, когда вы загружаете объект из базы данных, вы знаете, что вам придется отображать почти весь графический объект в память. В любом случае вам придется искать все эти внешние ключи позже - быстрее сделать все это, как один запрос, а не позволить ленивой загрузке выполнять эту работу. См. here для получения дополнительной информации.

Это должно выглядеть примерно так:

public ProductEntity GetProductById(string itemNumber, int storeNumber) { 
    var product = _unitOfWork 
     .GetProductRepository(storeNumber) 
     .GetQueryable() 
     .Include(p => p.IM_BARCOD) 
     .Include(p => p.IM_GRID_DIM_1) 
     .Include(p => p.IM_GRID_DIM_2) 
     .Include(p => p.IM_GRID_DIM_3) 
     .Include(p => p.IM_PRC) 
     .FirstOrDefault(p => p.ITEM_NO == itemNumber); 

    return product == null ? null : ProductEntity.FromEfObject(product); 
} 

Обратите внимание, что если эти внешние ключи имеют свои собственные внешние ключи (т.е. IM_BARCOD имеет коллекцию IM_OtherType), которые вы также должны отображаться в вашей модели ProductEntity, вы должны также включать их. Вы можете сделать это в соответствии, как это:

.Include(p => p.IM_BARCOD.Select(b => b.IM_OTHERTYPE)) 
+0

В этом случае я хотел бы сделать. Где (p => p.ITEM_NO == itemNumber) до включения, правильно? Я протестировал это, и он прошел от ~ 8 секунд до 20 секунд, поэтому я вернулся к отладке без включений. –

+0

Нет, вам не нужно, где раньше. С EF весь этот код преобразуется в один SQL-запрос - на самом деле он не выполняется на C#. Порядок операторов не имеет значения, поскольку в конце он превращается в один и тот же SQL-запрос. –

+0

Хорошо, что это хорошо. Я знал, что он скомпилирован по одному запросу, но не знал, что он достаточно умен, чтобы заказать что-то. Сортичный вопрос, но если бы у меня было несколько инструкций Where, это оптимизировало бы порядок для меня? –

Смежные вопросы