EF 4.1 заменить строку? - PullRequest
       26

EF 4.1 заменить строку?

1 голос
/ 12 сентября 2011

У меня есть проблема, когда внутри класса я хочу обновить или добавить строку в зависимости от записи, существующей внутри БД.В этом, вместо того, чтобы иметь методы «Create / Update», у меня есть метод «Save (entity)».

Идея состоит в том, что сущность проверяется по базе данных, если она существует, то это обновление, если она неТогда это, очевидно, создание.

При использовании EF 4.1 проблема заключается в том, что, как только я читаю строку из базы данных через тот же контекст, это, в свою очередь, создает кэш памяти этой строки.Затем, когда я пытаюсь заменить строку с помощью, скажем, подпрограммы присоединения / добавления, она, очевидно, выдаст исключение вокруг уже существующей строки и т. Д. (Так как она пытается принудительно вызвать и currentRow, и newRow в одну и ту же таблицу и завершится неудачно при сверке).

Мой обходной путь для этого в основном заключается в использовании .Remove () в контексте, который помечает строку для удаления из памяти, а затем повторно добавляет ее в той же транзакции USING.

 var ctx = new SecurityContext(this.ConnectionString);
        using(ctx)
        {
            var dbEntry = (ctx.Accounts.Where(a => a.AccountId == entity.AccountId || a.Username == entity.Username)).FirstOrDefault();
            ctx.Accounts.Remove(dbEntry);

            if (dbEntry != null)
            {
                ctx.Entry(entity).State = EntityState.Added;
            } else
            {
                ctx.Accounts.Add(entity);
                ctx.Entry(entity).State = EntityState.Added;
            }


            ctx.SaveChanges();
        }

Мой вопрос - это типичный маршрут?или есть много умнее / чище?

Ответы [ 4 ]

0 голосов
/ 12 сентября 2011

Возможно, вы сформулировали вопрос, но у вас странный подход.

  1. Если вы выбираете и обновляете сущность на том же уровне приложения, вам не следует заново создавать свой контекст при сохранении, просто держитесь за ссылку на контекст, который извлекает сущность, и она будет отслеживать изменения в вашей сущности, и вы просто вызываете SaveChanges () в том же контексте. В противном случае вы боретесь с его фундаментальным замыслом.

  2. Вы должны привыкнуть заключать вас в транзакции с помощью SaveChanges (). Если SaveChanges () вызывает изменения / вставки в более чем 1 строку в базе данных, вы рискуете сохранить частичные изменения. Вы всегда должны сохранять все или ничего.

    с использованием (TransactionScope ts = new TransactionScope ()) {
    ctx.SaveChanges ();
    ts.Complete (); }

  3. Если вы разрабатываете трехуровневое приложение, использующее, возможно, wcf для среднего уровня, и, следовательно, сериализуете сущность от клиента, вы можете просто добавить новое свойство «IsNew», через которое проходит клиент. Если не новый, вы должны использовать Attach (). например, если IsNew, то ctx.Accounts.Add (entity), иначе ctx.Accounts.Attach (entity)

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

Пункт 4 довольно сложный, и есть ряд подходов, которые я слишком сложно описать. Но будьте осторожны, если вы прибегаете к подходу там, где вы проверяете его существование, затем решаете добавить / прикрепить, убедитесь, что вы заключили это в транзакцию, так как в противном случае есть вероятность, что новый объект будет добавлен другим пользователем / процессом между вы проверяете (с помощью Where ()) и SaveChanges ().

0 голосов
/ 12 сентября 2011

Хорошо, я думаю, что это был случай, когда я был тупицей. Я понял, что конфликт мог возникнуть, когда две записи создавались одинаково, за исключением того, что AccountId были разными (учитывая, что они были созданы с помощью класса вызывающего),Я изменил DAO, чтобы он был ниже, и теперь он работает.

Если хотите, выделите код:)

    public class AccountDAO : BaseDAO<Account>
{
    public AccountDAO(string connectionString) : base(connectionString)
    {
    }
    public override Account Save(Account entity)
    {
        var ctx = new SecurityContext(this.ConnectionString);
        using(ctx)
        {
            //var dbEntry = (ctx.Accounts.Where(a => a.AccountId == entity.AccountId || a.Username == entity.Username)).FirstOrDefault();
            var dbEntry = (ctx.Accounts.Any(a => a.AccountId == entity.AccountId || a.Username.Contains(entity.Username)));
            //ctx.Accounts.Remove(entity);
            if(!dbEntry)
            {
                ctx.Accounts.Add(entity);
                ctx.Entry(entity).State = EntityState.Added;
            } else
            {
                var currEntity = Read(entity);
                entity.AccountId = currEntity.AccountId;
                ctx.Accounts.Add(entity);
                ctx.Entry(entity).State = EntityState.Modified;
            }

            ctx.SaveChanges();
        }
        return entity;
    }
    public override Account Read(Account entity)
    {
        using (var ctx = new SecurityContext(this.ConnectionString))
        {
            var newEntity = (ctx.Accounts.Where(a => a.AccountId == entity.AccountId || a.Username.Contains(entity.Username))).FirstOrDefault();
            return newEntity;
        }            
    }
    public override void Delete(Account entity)
    {
        using (var ctx = new SecurityContext(this.ConnectionString))
        {
            var ent = Read(entity);
            ctx.Entry(ent).State = EntityState.Deleted;
            ctx.Accounts.Remove(ent);
            ctx.SaveChanges();
        }     
    }
}
0 голосов
/ 12 сентября 2011

см. Образец

 private void Save(Action<Controls.SaveResult> saveResult)
    {
        if (SavableEntity.EntityState != EntityState.Unmodified)
        {
            if (SavableEntity.EntityState == EntityState.Detached || SavableEntity.EntityState == EntityState.New)
                this.SetIsBusy("Creating new...");
            else
                this.SetIsBusy("Saving changes...");
            DomainContext.SavePartial(SavableEntity, p =>
            {
                this.ReleaseIsBusy();
                if (p.HasError)
                {
                    var res = new Controls.SaveResult() { HasErrors = true };
                    if (saveResult != null)
                        saveResult(res);
                    if (this.EntitySaved != null)
                    {
                        EntitySaved(this, new SavedEventArgs() { result = res });
                    }
                    p.MarkErrorAsHandled();
                }
                else
                {
                    Messenger.Default.Send<T>(this.SavableEntity);
                    var res = new Controls.SaveResult() { HasErrors = false };
                    if (saveResult != null)
                        saveResult(res);
                    if (this.EntitySaved != null)
                        EntitySaved(this, new SavedEventArgs() { result = res });
                    if (this.CloseAfterSave)
                        this.RaiseRequestToClose();
                    this.RaisePropertyChanged("Title");
                }
                RaisePropertyChanged("SavableEntity");

            }, false);
        }

    }
0 голосов
/ 12 сентября 2011

Я считаю, что этот код должен работать, используя Attach вместо Add:

var  ctx = new SecurityContext(this.ConnectionString); 
using(ctx) 
{ 
    ctx.Accounts.Attach(entity);
    ctx.Entry(entity).State = ctx.Accounts.Any(
        a => a.AccountId == entity.AccountId || 
        a.Username == entity.Username) ? 
        EntityState.Modified : EntityState.Added;
    ctx.SaveChanges(); 
} 

Простите за странную упаковку - хотел, чтобы он поместился на странице без прокрутки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...