Entity Framework 4.1 Проблемы с обновлением свойств внешнего ключа - PullRequest
4 голосов
/ 24 августа 2011

Я работаю над приложением, использующим Entity Framework 4.1 с DbContext API, в автономной среде. У меня есть две основные сущности, человек и степень. Степень имеет обязательное отношение «один ко многим» с человеком.

Проблема возникает, когда я обновляю свойство DegreeId в сущности Person на другое значение. Когда я сохраняю изменения, EF генерирует оператор Update для фактической таблицы Degree. Это, в свою очередь, приводит к нарушению параллелизма, когда два или более пользователей используют приложение. Мне удалось найти проблему при использовании SQL Profiler. Я пробовал несколько вариантов конфигурации с использованием Fluent API, но, похоже, ничто не подавляет дополнительный оператор Update в таблице Degree.

Вот мои сущности:

public partial class Person
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public Nullable<int> DegreeId { get; set; }

        public Degree Degree { get; set; }
    }

public partial class Degree 
    {
        public int DegreeId { get; set; }
        public string Name { get; set; }
    }

В своем классе репозитория я загружаю граф объекта Person следующим образом:

public override Person GetById(int id)
   {
         return DataContext.People
                    .Include(d => d.Degree)
                    .FirstOrDefault(x => x.PersonId == id);
   }

На моем слое службы я получаю запись о человеке, а затем обновляю свойство DegreeId до определенного значения. Примечание: метод UnitOfWork.Commit предоставляет SaveChanges для DbContext.

using (var unitOfWork = IoC.Resolve<IUnitOfWork>())
  {
        var personRepository = new PersonRepository(unitOfWork);
        var person = personRepository.GetById(240);
        person.DegreeId = 1;
        personRepository.Update(person);
        unitOfWork.Commit();
  }

Мой метод обновления репозитория присоединяет сущность человека и помечает состояние сущности как измененное:

var state = DataContext.Entry(entity).State;
dbSet.Attach(entity);
DataContext.Entry(entity).State = EntityState.Modified; 

Вот инструкция SQL, найденная в сеансе Profiler:

exec sp_executesql N'declare @p int
update [Client].[Degree]
set @p = 0
where (([DegreeId] = @0) and ([RowVersion] = @1))
select [RowVersion]
from [Client].[Degree]
where @@ROWCOUNT > 0 and [DegreeId] = @0',N'@0 int,
@1 binary(8)',@0=1,@1=0x0000000000004469

Кто-нибудь знает, как остановить EF от отправки этого оператора обновления на SQL Server? Есть ли что-то очевидное в моей конфигурации сущностей, которое заставляет EF предполагать, что Степень также затронута?

Спасибо.

1 Ответ

2 голосов
/ 25 августа 2011

Мне удалось найти причину этой проблемы и предотвратить ее возникновение, но я не могу точно объяснить, почему она возникла.

Мои таблицы содержат столбец TimeStamp и соответствующее свойство в базовом классе для моих сущностей. Я не отображал базовый класс в своем первоначальном вопросе, потому что он включает в себя только RowVersion и другие свойства аудита, которые, как я предполагал, не имеют значения. Можно было бы подумать, что я узнал бы, не предполагая ничего о Entity Framework.

Вот мое определение базового класса для сущности Степень:

public abstract class EntityBase : ValidableObject, IEntityBase
{
    public virtual byte[] RowVersion { get; protected set; }
    public virtual DateTime? CreateDate { get; set; }
    public virtual string CreateUser { get; set; }
    public virtual DateTime? ModifyDate { get; set; }
    public virtual string ModifyUser { get; set; }
}

Вот моя конфигурация контекстной модели для объекта Степень:

internal class DegreeConfiguration : EntityTypeConfiguration<Degree>
    {
        internal DegreeConfiguration()
            : base()
        {
            ToTable("Degree", "dbo");
            Property(x => x.RowVersion).IsRowVersion();
        }
    }

Из-за требований моего приложения я должен загрузить сущность Person, используя метод Include, чтобы быстро загрузить сущность Degree, чтобы граф объекта был полностью заполнен, когда потребитель запрашивает объект.

return ctx.People.Include(p => p.Degree).Where(x => x.PersonId == id).First();

Когда свойство DegreeId объекта Person изменяется и присоединяется к контексту, генерируется следующий оператор Update после вызова SaveChanges ():

exec sp_executesql N'declare @p int
update [dbo].[Degree]
set @p = 0
where (([DegreeId] = @0) and ([RowVersion] = @1))
select [RowVersion]
from [dbo].[Degree]
where @@ROWCOUNT > 0 and [DegreeId] = @0',N'@0 int,
@1 binary(8)',@0=2,@1=0x00000000000007DF

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

Чтобы исключить создание оператора Update для свойства навигации Degree, я прокомментировал проверку параллелизма в конфигурации модели следующим образом:

internal class DegreeConfiguration : EntityTypeConfiguration<Degree>
    {
        internal DegreeConfiguration()
            : base()
        {
            ToTable("Degree", "dbo");
            //Property(x => x.RowVersion).IsRowVersion();
        }
    }

После повторного выполнения процесса EF больше не создает проблемный оператор Update.

Я провел значительное количество поисков как на сайте MS для EF 4.1, так и общих запросов Google. Я не могу придумать никаких конкретных объяснений.

Спасибо.

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