Понимание объектов при использовании Entity Framework - PullRequest
3 голосов
/ 16 ноября 2011

Я увидел что-то странное, с той точки зрения, что я не ожидал этого.

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

MyRepository db = new MyRepository();

// let's get them all
IQueryable<TblCals> cals = db.FindAllCalendars();

// now that we have everything, let's split up
List<TblCals> masters = cals.Where(x => x.IsMaster).ToList();
List<TblCals> childs =  cals.Where(x => x.Parent > 0).ToList();

// Let's change the name of the masters
foreach(var c in masters) c.Name = String.Concat("Master: ", c.Name);

// Let's send everything to the View
return View(new model() { masters, childs });

Теперь проблема :

Почему имя, изменяющееся в List masters, также меняет имя объектов, находящихся в childs?

Когда я делаю List<TblCals> masters = cals.Where(x => x.IsMaster).ToList();, не должно ли это скопировать элементы вместо того, чтобы ссылаться на них только из объекта cals?

Что я могу сделать , чтобы предотвратить это поведение?

Я мог бы просто сделать:

MyRepository db = new MyRepository();

// let's get them all
IQueryable<TblCals> cals = db.FindAllCalendars();

// now that we have everything, let's split up
List<TblCals> masters = db.FindAllCalendarsThatAreMasters().ToList();
List<TblCals> childs =  db.FindAllCalendarsThatHaveParent().ToList();

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


Основная идея, стоящая за всем этим, состоит в том, чтобы убедиться, что это безопасно:

public IEnumerable<JK_Users> ListAllUsers() {
    // Check / Add to memcache
    string key = "ListAllUsers";
    var users = MemcachedLayer.Get<IEnumerable<JK_Users>>(key);

    if(key == null)
        users = db.Tbl_Users.CachedQuery(key);

    return users;
}

public IEnumerable<JK_Users> FindUserById(decimal id) {
    // call ListAllUsers() instead Repository
    return this.ListAllUsers().FirstOrDefault(x => x.user_id == id);
}
public IEnumerable<JK_Users> FindUserByUsername(string username) {
    // call ListAllUsers() instead Repository
    return this.ListAllUsers().FirstOrDefault(x => x.username == username);
}

или мне нужно позвонить

public JK_Users FindUserById(decimal id) {
    return db.Tbl_Users.FirstOrDefault(x => x.user_id == id);
}
public JK_Users FindUserByUsername(string username) {
    return db.Tbl_Users.FirstOrDefault(x => x.username == username);
}

Так что я могу использовать кэш более эффективно.

1 Ответ

3 голосов
/ 16 ноября 2011

Нет, коллекции содержат ссылки только на ссылочные типы. Здесь нет ничего специфичного для EF:

List<StringBuilder> list1 = new List<StringBuilder>();
List<StringBuilder> list2 = new List<StringBuilder>();
StringBuilder builder = new StringBuilder();
list1.Add(builder);
list2.Add(builder);
list1[0].Append("Foo");
Console.WriteLine(list2[0]); // Still prints Foo...

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

Это действительно логический подход - думать о ценностях, связанных с сущностями; ваши два запроса соответствуют одним и тем же объектам для некоторых результатов ... почему вы хотите, чтобы эти два результата для одного и того же объекта были независимыми объектами? Если кто-то ссылается на «Джон Скит из Переполнения стека» и «Джон Скит из Tilehurst, Рединг, Великобритания», то это один и тот же человек - изменение (скажем) возраста с помощью одного результата должно изменить возраст при просмотре через другой результат.

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