LINQ to SQL: присоединять или не присоединять - PullRequest
9 голосов
/ 21 апреля 2010

Так что у меня действительно трудное время, чтобы понять, когда я должен прикрепляться к объекту, а когда я не должен прикрепляться к объекту. Прежде всего, вот небольшая диаграмма моей (очень упрощенной) объектной модели.

Schema

В моем DAL я создаю новый DataContext каждый раз, когда я выполняю операцию, связанную с данными. Скажем, например, я хочу сохранить нового пользователя. На своем бизнес-уровне я создаю нового пользователя.

var user = new User();
user.FirstName = "Bob";
user.LastName = "Smith";
user.Username = "bob.smith";
user.Password = StringUtilities.EncodePassword("MyPassword123");
user.Organization = someOrganization; // Assume that someOrganization was loaded and it's data context has been garbage collected.

Теперь я хочу пойти сохранить этого пользователя.

var userRepository = new RepositoryFactory.GetRepository<UserRepository>();
userRepository.Save(user);

Neato! Вот моя логика сохранения:

public void Save(User user)
{
 if (!DataContext.Users.Contains(user))
 {
  user.Id = Guid.NewGuid();
  user.CreatedDate = DateTime.Now;
  user.Disabled = false;

  //DataContext.Organizations.Attach(user.Organization);
  DataContext.Users.InsertOnSubmit(user);
 }
 else 
 {
  DataContext.Users.Attach(user);
 }

 DataContext.SubmitChanges();

 // Finished here as well.
 user.Detach();
}

Итак, мы здесь. Вы заметите, что я закомментировал бит, где DataContext присоединяется к организации. Если я присоединяюсь к организации, я получаю следующее исключение:

NotSupportedException: Была предпринята попытка присоединить или добавить сущность, которая не нова, возможно, имея был загружен из другого DataContext. Это не поддерживается.

Хм, это не работает. Позвольте мне попробовать это без присоединения (то есть закомментируйте эту строку о присоединении к организации).

DuplicateKeyException: Невозможно добавить объект с ключом, который уже используется.

WHAAAAT? Я могу только предположить, что это попытка вставить новую организацию, которая, очевидно, является ложной.

Так в чем же дело, ребята? Что я должен делать? Каков правильный подход? Кажется, что L2S делает это немного сложнее, чем должно быть ...

РЕДАКТИРОВАТЬ: Я только что заметил, что если я пытаюсь посмотреть ожидающий набор изменений (dataContext.GetChangeSet ()), я получаю то же исключение NotSupportedException, которое я описал ранее !! Какого черта, L2S?!

Ответы [ 3 ]

7 голосов
/ 06 мая 2010

Это может работать не совсем так, как это, но вот способ, которым я это концептуализирую: когда вы вызываете объект из DataContext, одна из вещей, которые делает Linq, - отслеживание изменений этого объекта с течением времени, чтобы он знал сохранить обратно, если вы отправите изменения. Если вы потеряете этот исходный контекст данных, вызванный из него объект Linq не будет иметь истории того, что изменилось в нем с момента вызова из БД.

Например:

DbDataContext db = new DbDataContext();
User u = db.Users.Single( u => u.Id == HARD_CODED_GUID );
u.FirstName = "Foo";
db.SubmitChanges();

В этом случае, поскольку объект User был вызван из контекста данных, он отслеживал все изменения в «u» и знает, как отправить эти изменения в БД.

В вашем примере у вас был объект User, который был сохранен где-то (или передан из другого места и не имеет своего исходного DataContext, из которого он был вызван). В этом случае вы должны присоединить его к новому контексту данных.

User u; // User object passed in from somewhere else
DbDataContext db = new DbDataContext();
u.FirstName = "Foo";
DbDataContext.Users.Attach( u );
db.SubmitChanges();

Поскольку отношения между пользователем и организацией - это просто GUID (OrganizationId) в вашей модели данных, вам нужно только присоединить объект пользователя.

Я не уверен насчет вашего кода скаффолдинга, но может быть что-то вроде этого:

    private const Guid DEFAULT_ORG = new Guid("3cbb9255-1083-4fc4-8449-27975cb478a5");
    public void Save(User user)
    {
        if (!DataContext.Users.Contains(user))
        {
            user.Id = Guid.NewGuid();
            user.CreatedDate = DateTime.Now;
            user.Disabled = false;
            user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
                                               // via a GUID, not by assigning an
                                               // Organization object

            DataContext.Users.InsertOnSubmit(user);
        }
        else
        {
            DataContext.Users.Attach(user);
        }

        DataContext.SubmitChanges();

    }
1 голос
/ 21 апреля 2010

Таким образом, «присоединение» используется, когда вы берете объект, который существует из базы данных, отсоединяете его (скажем, перенося его через веб-сервис где-то еще) и хотите поместить его обратно в базу данных. Вместо вызова .Attach (), вместо этого вызовите .InsertOnSubmit (). Вы почти там концептуально, вы просто используете неправильный метод, чтобы делать то, что вы хотите.

0 голосов
/ 04 апреля 2014

Я использовал большую таблицу с 400+ столбцами. Я не собираюсь наносить на карту и проверять все это!

Получить исходный объект из базы данных и прикрепить его с исправленным объектом. Просто убедитесь, что возвращаемый объект полностью заполнен, иначе он заменит БД пробелами!

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

public void Save(User user)
{

    if (!DataContext.Users.Contains(user))
    {
        user.Id = Guid.NewGuid();
        user.CreatedDate = DateTime.Now;
        user.Disabled = false;
        user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
                                           // via a GUID, not by assigning an
                                           // Organization object

        DataContext.Users.InsertOnSubmit(user);
    }
    else
    {
        var UserDB = DataContext.Users.FirstOrDefault(db => db.id == user.id); //Costs an extra call but its worth it if oyu have 400 columns!
        DataContext.Users.Attach(user, userDB); //Just maps all the changes on the flu
    }

    DataContext.SubmitChanges();

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