EF4/WCF SaveChanges() Передовая практика

Вот как мы реализуем универсальную службу Save() в WCF для наших сущностей EF. TT делает работу за нас. Несмотря на то, что у нас нет никаких проблем с этим, я ненавижу предполагать, что это лучший подход (даже если это может быть так). Вы, ребята, кажетесь чертовски умными и полезными, поэтому я подумал, что задам вопрос:

Есть ли способ лучше?

[OperationContract]
public User SaveUser(User entity)
{
    bool _IsDeleted = false;
    using (DatabaseEntities _Context = new DatabaseEntities())
    {
        switch (entity.ChangeTracker.State)
        {
            case ObjectState.Deleted:
                //delete
                _IsDeleted = true;
                _Context.Users.Attach(entity);
                _Context.DeleteObject(entity);
                break;
            default:
                //everything else
                _Context.Users.ApplyChanges(entity);
                break;
        }
        // now, to the database
        try
        {
            // try to save changes, which may cause a conflict.
            _Context.SaveChanges(System.Data.Objects.SaveOptions.None);
        }
        catch (System.Data.OptimisticConcurrencyException)
        {
            // resolve the concurrency conflict by refreshing 
            _Context.Refresh(System.Data.Objects.RefreshMode.ClientWins, entity);
            // Save changes.
            _Context.SaveChanges();
        }
    }
    // return
    if (_IsDeleted)
        return null;
    entity.AcceptChanges();
    return entity;
}

person Jerry Nixon    schedule 23.06.2011    source источник
comment
Может быть, лучшей практикой является не писать код, который вызовет OptimisticConcurrencyExceptions? Что вызывает их появление? Я никогда не видел его раньше.   -  person StriplingWarrior    schedule 24.06.2011
comment
У @StriplingWarrior есть хорошая мысль ... вы когда-нибудь ловили это исключение из своего кода?   -  person BentOnCoding    schedule 24.06.2011
comment
Только в ситуации отладки. Это правда.   -  person Jerry Nixon    schedule 24.06.2011
comment
@JerryNixon-MSFT хороший подход, как узнать, был ли изменен объект EF? Потому что класс сущности POCO не имеет свойства для установки этого, или я ошибаюсь? Спасибо, что поделился.   -  person VAAA    schedule 04.11.2013
comment
У отслеживаемых объектов @VAAA есть способ. Да.   -  person Jerry Nixon    schedule 06.11.2013


Ответы (2)


Почему вы делаете это с сущностями самоотслеживания? Что в этом было не так:

[OperationContract]
public User SaveUser(User entity)
{
    bool isDeleted = false;
    using (DatabaseEntities context = new DatabaseEntities())
    {
        isDeleted = entity.ChangeTracker.State == ObjectState.Deleted;
        context.Users.ApplyChanges(entity); // It deletes entities marked for deletion as well

        try
        {
            // no need to postpone accepting changes, they will not be accepted if exception happens
            context.SaveChanges(); 
        }
        catch (System.Data.OptimisticConcurrencyException)
        {
            context.Refresh(System.Data.Objects.RefreshMode.ClientWins, entity);
            context.SaveChanges();
        }
    }

    return isDeleted ? null : entity;
}
person Ladislav Mrnka    schedule 24.06.2011
comment
Привет, как узнать, удалено состояние объекта или нет? Как вы это отслеживаете на клиенте. Потому что я предположил, что вы отправляете объект из WCF клиенту, но у объекта нет свойства для установки состояния, не так ли? Большое спасибо - person VAAA; 04.11.2013

Если я не ошибаюсь, люди обычно не предоставляют свои объекты Entity Framework непосредственно в службе WCF. Entity Framework обычно рассматривается как уровень доступа к данным, а WCF — скорее интерфейсный уровень, поэтому они размещаются на разных уровнях.

Объект передачи данных (DTO) используется в методах WCF. Обычно это POCO, который вообще не имеет никакого отслеживания состояния. Затем DTO сопоставляется с сущностью либо вручную, либо с помощью такой среды, как AutoMapper.

Как правило, клиенты должны знать, «добавляют» они или «обновляют» объект, и лично я бы предпочел, чтобы это были две отдельные операции в интерфейсе службы. Кроме того, я бы определенно потребовал, чтобы они использовали отдельный метод для удаления объекта. Однако, если вам абсолютно необходимо общее «Сохранить», вы должны иметь возможность определить, является ли объект, который вам дали, «новым» или нет, на основе наличия (или отсутствия) значения первичного ключа.

Много кода можно поместить в универсальную утилиту. Например, предположим, что ваш шаблон T4 создает атрибуты для значений ключей ваших сущностей, вы можете автоматически определить, присутствуют ли значения ключей, и соответственно выполнить вставку/обновление. Кроме того, блок try SaveChanges catch retry, который вы используете, хотя, вероятно, и не нужен, можно легко поместить в простой служебный метод, чтобы сделать его более СУХИМ.

person StriplingWarrior    schedule 23.06.2011
comment
Мы не делаем это так, как вы описываете, и это работает потрясающе. Сущности с самостоятельным отслеживанием в общей сборке между уровнями не вызывают затруднений. - person Jerry Nixon; 13.04.2012
comment
@JerryNixon: Итак, вы отправляете сущности клиента WCF напрямую, а клиент изменяет сущности и отправляет их обратно в службу? Очень круто, что он работает так плавно. Спасибо, что поделился. - person StriplingWarrior; 13.04.2012
comment
Да! не только это, но я получаю полную точность в объектах, потому что они являются внешними, а не прокси на клиенте. АКА у них могут быть методы! - person Jerry Nixon; 15.05.2012
comment
@StriplingWarrior У меня есть такой подход, когда WCF отправляет клиенту класс POCO (фактически объект EF). Этот POCO не имеет никакого отслеживания состояния и т. д. Как я могу узнать, был ли объект удален или изменен в службе WCF при сохранении? Есть ли образец, на который я могу посмотреть? Спасибо, что поделились.+ - person VAAA; 04.11.2013
comment
@VAAA: меня немного смущает ваш вопрос. Предположительно, если вы используете POCO без отслеживания состояния, вы не удаляете его, вызывая для него .Delete(), верно? Таким образом, его удаление должно заключаться в вызове метода Delete в службе WCF и передаче объекта (или его идентификатора) методу в качестве параметра. Поскольку это не отслеживание состояния, вы также не можете знать, был ли объект изменен — вы можете только сравнить его с тем, что находится в вашем хранилище данных, и посмотреть, не совпадает ли он. - person StriplingWarrior; 04.11.2013
comment
Вы совершенно правы, так оно и есть. Может быть, я не объяснил себя. :) Любой веб-ресурс о том, как этот сервис POCO + EF + WCF должен работать наилучшим образом? - person VAAA; 04.11.2013
comment
@VAAA: я ничего не знаю. Лучшее, что я могу сделать, это указать вам на Google. - person StriplingWarrior; 05.11.2013