Должен ли я ввести избыточность в дизайн модели

1

Я пытаюсь создать новую систему отслеживания продаж. Простейшая версия моих моделей данных:

public class Sale 
{
    public int SaleId { get; set; }
    public DateTime CompletedDateTime { get; set; }

    public virtual List<SaleItem> SaleItems { get; set; }

    public decimal Total
    {
       get
        {
            return SaleItems.Sum(i => i.Price);
        }
    }
}


public class SaleItem 
{
    public int SaleItemId { get; set; }
    public decimal Price { get; set; }
    public int SaleId { get; set; }
    public virtual Sale Sale { get; set; }

}

Сейчас я пишу несколько отчетов, в которых общая стоимость продаж находится между указанным периодом. Для этого у меня есть следующий код:

List<Sale> dailySales = db.Sales
                          .Where(x => DbFunctions.TruncateTime(x.CompletedDateTime) >= fromParam)
                          .Where(x => DbFunctions.TruncateTime(x.CompletedDateTime) <= toParam)
                          .ToList();

decimal total = dailySales.Sum(x => x.Total);

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

Следующее позволило бы мне выполнить всю обработку в базе данных, однако это невозможно сделать, поскольку у БД нет представления для Total, поэтому EF выдает ошибку:

Decimal total = db.Sales.Sum(x=>x.Total);

Это приводит меня к моему вопросу. Я мог бы задать мне модель следующим образом, и каждый раз, когда я добавляю SaleItem, убедитесь, что обновляю Total:

public class Sale 
{
    ...    
    public decimal Total { get; set; }
}

Тогда это позволит мне запрашивать базу данных по мере необходимости, и я предполагаю, что она будет менее ресурсоемкой. С другой стороны, это то, что я уменьшил избыточность в базе данных. Является ли последний способ, лучший метод борьбы с этим или есть альтернативный метод, который я даже не считал лучшим?

  • 0
    Это зависит от того, читаешь ли ты чаще или пишешь чаще
Теги:
entity-framework
asp.net-mvc
database-design
design

2 ответа

1

Это зависит от многих факторов. Например, как часто вам потребуется доступная сумма "Всего"? И сколько SaleItems обычно есть в продаже?

Если речь идет, скажем, о продаже супермаркета, где у вас есть... скажем... максимум максимум 200 предметов. Это вполне нормально, просто быстро вычислить его на лету. Опять же, если это когда-либо будет сопоставлено с РСУБД, и если у вас есть все SalesItems в одной таблице, то иметь индекс по внешнему ключу (который связывает каждый отдельный SaleItem с его продажей) является обязательным, иначе производительность будет огромной когда вы начнете прокладывать миллионы транзакций.

Отвечая на вторую половину вашего вопроса, избыточность не всегда является плохим... вам просто нужно убедиться, что если каждая продажа когда-либо понадобится изменить свой список, то в конце ее пересчитывается Total. Это немного опасно (избыточность всегда имеет это привязанное бремя), но вам просто нужно убедиться, что все, что может изменить Sale, делает это в некотором смысле (возможно, даже с триггером в СУБД), что сумма будет автоматически пересчитана.

Надеюсь, поможет!

  • 0
    Это очень хороший совет. Я бы добавил, что управляемая избыточность - это нормально, но не начинайте с предположения, что она вам понадобится. Посредством нагрузочного тестирования выясните, есть ли у вас проблемы с производительностью, не думайте, что она будет обнаружена без ее тестирования.
0

Вы правы, это гораздо более эффективно для вычисления итогов на стороне БД, а не для загрузки всего списка и расчета его в приложении.

Я думаю, что вам не хватает, что вы можете сделать запрос LINQ, который получает SUM связанных дочерних объектов.

using (var ctx = new MyDbContext())
{
    var totalSales = ctx.Sales
        .Select(s => s.SaleItems.Sum(si => si.Price)) // Total of each Sale
        .Sum(tsi => tsi); // Sum of the total of each sale
}

Вы можете, конечно, сформировать запрос, чтобы добавить дополнительную информацию, проецировать результат в анонимный класс или в класс, созданный ad-hoc для этой цели.

Конечно, этот запрос EF будет переведен в SQL-запрос и выполнен на стороне сервера.

Когда вы начинаете использовать LINQ to EF, не очень очевидно, как получить то, что вы хотите, но в большинстве случаев вы можете это сделать.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню