Параллелизм Entity Framework WCF - PullRequest
       11

Параллелизм Entity Framework WCF

4 голосов
/ 06 декабря 2010

У меня есть служба WCF, которая выполняет вызовы в мои классы Entity Framework Repository для доступа к данным. Я использую Entity Framework 4 CTP и использую мои собственные объекты POCO, а не объекты автоматически генерируемых объектов.

Время жизни контекста ограничено вызовом метода. Для методов Select / Insert и Update я создаю контекст и располагаю его тем же методом, возвращая отсоединенные объекты сущностей.

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

public static Sale Update(Sale sale)
{
    using (var ctx = new DBContext())
    {
        var SaleToUpdate =
            (from t in ctx.Sales where t.ID == sale.ID select t).FirstOrDefault();
        if (SaleToUpdate == null) throw new EntityNotFoundException();
        ctx.Sales.ApplyCurrentValues(sale);
        ctx.SaveChanges();
        return sale;
    }
}

Это работает нормально, но, поскольку я работаю в автономном режиме, не возникает исключений, если запись была изменена с момента ее получения. Это может вызвать проблемы с параллелизмом.

Каков наилучший способ решить эту проблему, если вы используете платформу сущностей поверх WCF и не сохраняете глобальный контекст?

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

РЕДАКТИРОВАТЬ: Используя предложение Ладислава Мрнки о полях RowVersion в моих сущностях, каждая из моих сущностей теперь имеет поле с именем Версия типа RowVersion. Затем я изменил свой метод обновления, чтобы он выглядел следующим образом.

public static Sale Update(Sale sale)
{
    using (var ctx = new DBContext())
    {
        var SaleToUpdate =
            (from t in ctx.Sales where t.ID == sale.ID select t).FirstOrDefault();
        if (SaleToUpdate == null) throw new EntityNotFoundException();
        if (!sale.Version.SequenceEqual(SaleToUpdate .Version)) 
            throw new OptimisticConcurrencyException("Record is out of date");
        ctx.Sales.ApplyCurrentValues(sale);
        ctx.SaveChanges();
        return sale;
    }
}

Кажется, это работает, но если я должен делать это по-другому, пожалуйста, дайте мне знать. Я попытался использовать встроенные средства управления параллелизмом Entity Frameworks, установив фиксированный режим параллелизма полей версии, но, к сожалению, это не сработало, поскольку, когда я выполнял запрос, чтобы получить неизмененный SaleToUpdate, он брал свою версию и использовал ее для проверки параллельности. что, очевидно, актуально. Такое ощущение, что в фреймворке сущностей здесь чего-то не хватает.

Ответы [ 3 ]

3 голосов
/ 06 декабря 2010

Как уже упоминалось, рекомендуется использовать столбец с типом версии строки в таблице БД для проверки параллелизма, но как это реализовано в Code First:
При использовании Code First в CTP3 вам потребуетсяиспользуйте свободный API для описания того, какие свойства нуждаются в проверке параллелизма, но в CTP4 это можно сделать декларативно как часть определения класса с использованием атрибутов аннотации данных:

ConcurrencyCheckAttribute:

ConcurrencyCheckAttribute используется для указания того, что свойство имеет режим параллелизма «фиксированный» в модели.Фиксированный режим параллелизма означает, что это свойство является частью проверки параллелизма сущности во время операций сохранения и применяется только к скалярным свойствам:

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

    [ConcurrencyCheck]
    public string SalesPersonName { get; set; }    
}

Здесь ConcurrencyCheck будет включен дляSalesPersonName собственность.Однако, если вы решите включить в свой класс выделенное свойство Timestamp типа byte [], тогда TimestampAttribute определенно будет лучшим выбором для:

TimestampAttribute:

TimestampAttribute используется для указания того, что свойство byte [] имеет режим параллелизма «fixed» в модели и что его следует рассматривать как столбец timestamp в модели магазина (ненулевой байт [] в типе CLR).Этот атрибут применяется к скалярным свойствам типа byte [] только и только один атрибут TimestampAttribute может присутствовать на объекте.

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

    [Timestamp]
    public byte[] Timestamp { get; set; }
}

Здесь не только свойство Timestamp будет приниматься кактокен параллелизма, но также и код EF Сначала узнайте, что это свойство имеет тип хранения отметка времени , а также что это вычисляемый столбец , и мы не будем вставлять значения в это свойство, а скорее,значение будет вычислено на самом SQL Server.

3 голосов
/ 06 декабря 2010

Не используйте пользовательский номер версии.Используйте тип данных build in row вашей базы данных.Тип данных версии строки автоматически изменяется при каждом изменении записи.Например, MSSQL имеет тип данных Timestamp.Вы можете использовать столбец отметки времени в EF и установить его как фиксированный обработчик параллелизма (не уверен, как это сделать с EF Code First, но я полагаю, что свободный API имеет такую ​​возможность).Столбец отметки времени должен быть сопоставлен с объектом POCO в виде байтового массива (8 байтов).Когда вы вызываете свой метод обновления, вы можете самостоятельно проверить временную метку загруженного объекта и временную метку входящего объекта, чтобы избежать ненужного обращения к БД.Если вы не сделаете проверку самостоятельно, она будет обработана в EF путем установки условия where в операторе обновления.

0 голосов
/ 06 декабря 2010

Взгляните на Сохранение изменений и управление параллелизмом

из статьи:

try
{
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    Console.WriteLine("No conflicts. " +
    num.ToString() + " updates saved.");
}
catch (OptimisticConcurrencyException)
{
    // Resolve the concurrency conflict by refreshing the 
    // object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders);

    // Save changes.
    context.SaveChanges();
    Console.WriteLine("OptimisticConcurrencyException "
    + "handled and changes saved");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...