Сохранение изменений из доходности возврата через Entity Framework

У меня возникла проблема с получением Entity Framework для правильного сохранения результатов yield return. Чтобы проиллюстрировать проблему, я создал 2 набора методов, один из которых возвращает объект, а другой возвращает IEnumerable с помощью yield return. Журнал ссылается на UserProfile.

Набор 1:

public static UserProfile CreateUser()
{
    return new UserProfile() { 
                 UserId = Guid.Parse("60a3987c-0aa6-4a93-  a5d2-68c51122858b"), 
                 UserName = "jason"
               };
}
public static Journal CreateJournal(UserProfile userProfile)
{
    return new Journal() { UserProfile = userProfile };
}

Комплект 2:

public static IEnumerable<UserProfile> CreateUsers()
{
     yield return new UserProfile() { 
                          UserId = 
                             Guid.Parse("02cd1e9f-5947-4b08-9616-5b4f4033d074"), 
                          UserName = "john"
                      };
}
public static IEnumerable<Journal> CreateJournals(UserProfile userProfile)
{
    yield return new Journal() { UserProfile = userProfile };
}

TestSet1 и TestSet2 сохраняют результаты из Set1 и Set2 соответственно. TestSet1 работает, но TestSet2 выдает исключение нарушения ограничения PRIMARY KEY «PK_dbo.UserProfiles». Еще одно наблюдение - если я инициализирую список и возвращаю его вместо yield return, тогда он работает.

public static void TestSet1()
{
   var u = CreateUser();
   var j = CreateJournal(u);
   _db.UserProfiles.Add(u);
   _db.Journals.Add(j);
   _db.Commit();
}

public static void TestSet2()
{
   var uList = CreateUsers();
   var jList = CreateJournals(uList.ElementAt(0));
   _db.UserProfiles.Add(uList.ElementAt(0));
   _db.Journals.Add(jList.ElementAt(0));
   _db.Commit();
}

Что вы думаете о том, почему доходность в Set2 не работает? Спасибо


person KakkoiiMan    schedule 17.09.2012    source источник


Ответы (1)


Что происходит, поскольку вы фактически не материализуете список, вы создаете 2 объекта UserProfile. Вы можете увидеть это, если поставите точку останова на эту строку:

yield return new UserProfile() { UserId = Guid.Parse("02cd1e9f-5947-4b08-9616-5b4f4033d074"), UserName = "john"};

Вы заметите, что он вызывается дважды - поэтому вместо получения исходного UserProfile, созданного для uList, функция jList получает новый объект UserProfile. Теперь, хотя у них один и тот же Guid, они технически не равны, потому что являются ссылочными типами и не указывают на одно и то же место. Затем ChangeTracker увидит, что они не равны, и попытается создать 2 объекта в базе данных, один для UserProfiles.Add (), а затем второй, прикрепленный к вашему jList, с тем же Guid, и вот как вы получите свою ошибку .

Вы можете исправить свой код, вызвав ToList () в ваших CreateUsers в вашей функции Test2, материализуя список в памяти, чтобы все совпало.

public static void TestSet2()
{
   var uList = CreateUsers().ToList();
   var jList = CreateJournals(uList.ElementAt(0));
   _db.UserProfiles.Add(uList.ElementAt(0));
   _db.Journals.Add(jList.ElementAt(0));
   _db.Commit();
}

Дополнительную информацию о том, как работает ключевое слово yield, см. здесь, здесь и здесь

person Mark Oreta    schedule 17.09.2012
comment
Спасибо, Марк. Это привело меня к более глубокому пониманию того, как работает возврат доходности сейчас. - person KakkoiiMan; 17.09.2012
comment
Если вы действительно хотите знать, как работает yield return, откройте dll с помощью отражателя или ilspy и посмотрите, что компилятор делает с вашим нетронутым методом. - person Jim Wooley; 17.09.2012