簡體   English   中英

在調用SaveChanges()之前,在Add()之后從上下文中檢索實體

[英]Retrieving entity from context after Add() yet before SaveChanges() has been called

假設我有一個包含多個行的文件,每個行代表一個PersonPerson實體有一個identity column ,它也是primary key 假設一個Person可以在文件中重復,如果是,也許我想做最后一個條目勝利的場景。 在下面的示例中,我使用存儲庫通過社會保險號從數據庫中檢索人員。 問題是當同一個SSN再次出現在文件中時,存儲庫仍然返回null,即使技術上已經將具有該SSN的人添加到上下文中(尚未調用SaveChanges )。 我意識到我可以通過跟蹤自己已經添加了哪些Person對象來解決這個問題。 我想知道這種情況的最佳做法是什么。 謝謝。

foreach(row in fileRows)
{
    Person person = personRepository.GetBySSN(row.SSN);

    if(person == null)
    {
        //insert logic
    }
    else
    {
        //update logic
    }
}

personRepository.SaveChanges();

我想我可以給出我在這里給出的相同答案

要持久保存實體,通常會在上下文中將其添加到DbSet中。

例如

var bar = new Bar();
bar.Name = "foo";
var context = new Context();
context.Bars.Add(bar);

令人驚訝的是,查詢context.Bars ,無法找到剛剛添加的實體

var howMany = context.Bars.Count(b => b.Name == "foo");
// howMany == 0

context.SaveChanges() ,同一行將產生1

DbSet似乎沒有意識到改變,直到它們持久存在db。

幸運的是,每個DbSet都有一個Local屬性,就像DbSet本身一樣,但它反映了所有內存中的操作

var howMany = context.Bars.Local.Count(b => b.Name == "foo");
// howMany == 1

您還可以使用Local添加實體

context.Bars.Local.Add(bar);

並擺脫實體框架的奇怪行為。

修改GetBySSN ,如下所示:

public Person GetBySSN(string ssn) 
{
    Person p = context.ObjectStateManager
                      .GetObjectStateEntries(~EntityState.Deleted)
                      .Where(e => !e.IsRelationship)
                      .Select(e => e.Entity)
                      .OfType<Person>()
                      .SingleOrDefault(p => p.SSN = ssn);

    if (p == null) 
    {
        p = context.People.SingleOrDefault(p => p.SSN = ssn);
    }

    return p;
}

如果你想去Ladislav的路線並查詢ObjectStateManager,那么你可能希望使用如下的擴展方法:

public static IEnumerable<TEntity> LoadedEntities<TEntity>(this ObjectContext Context)
{
    return Context.ObjectStateManager
        .GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged)
        .Where(e => !e.IsRelationship).Select(e => e.Entity).OfType<TEntity>();
}

這將允許你做這樣的事情:

Person p = context.LoadedEntities<Person>().SingleOrDefault(p => p.SSN = ssn);
if (p != null)
    return p;
return context.People.SingleOrDefault(p => p.SSN = ssn);

我沒有找到任何關於最佳實踐的內容,所以我會發布我的解決方案。 我知道很多其他帖子都提到了這樣一個事實,即EF上下文提供了查看它並查看特定實體是否處於連接狀態的機制。 由於我通過存儲庫工作(我的業務層無法直接訪問EF上下文),我的選擇是將這種邏輯卸載到存儲庫中,或者嘗試在業務層中解決它。 我的感覺是,這項任務確實是一項業務問題而非數據訪問層問題。

Dictionary<string, Person> newPeople = ...

foreach(row in fileRows)
{
    Person person;

    //if the person is already tracked by the dictionary, work with it, if not use the repository
    if(!newPeople.TryGetValue(row.SSN, out person))
    {
        person = personRepository.GetBySSN(row.SSN);
    }

    if(person == null)
    {
        //insert logic

        //keep track that this person is already in line for inserting
        newPeople.Add(row.SSN, person);
    }
    else
    {
        //update logic
    }
}

personRepository.SaveChanges();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM