Как написать метод Linq на уровне данных? - PullRequest
1 голос
/ 06 апреля 2011

Я думаю о том, как использовать Linq в классической трехуровневой архитектуре проекта .net. Соответственно, Linq to SQL должен отображаться на уровне данных. Причина, по которой я выбираю Linq, заключается в том, что это сэкономит мне больше времени на коде, чем использование процедуры сохранения. Я провел некоторый поиск в Интернете о методе вставки / обновления / удаления в Linq, но не нашел подходящего метода для обновления записи с использованием сущностей. Обычно люди будут делать обновления следующим образом:

    public void UpdateUser(String username, String password, int userId) 
    {      
          using (var db = new UserDataContext()){
             var user = db.user.Single(p => p.Id = userId);
             user.Username = username;          
             user.Password = password;          
             db.SubmitChanges();      
           } 
    } 

Почему мы не используем сущность для передачи записи следующим образом:

public void Update(Application info)
{
    VettingDataContext dc = new VettingDataContext(_connString);
    var query = (from a in dc.Applications
                 where a.Id==info.Id
                 select a).First();
    query = info;
    try{
        dc.SubmitChanges();
        }
    catch(Exception e){
       //...
        }
 }

Но, к сожалению, приведенный выше код неверен из-за "query = info", но если я назначу каждое значение из "info" в "query", он будет работать нормально. как

query.firstName=info.firstName;
query.lastName=info.lastName;

Так что, если в этой таблице 40 полей, я должен написать 40 строк кода. Есть ли более простой способ сделать обновление? Надеюсь, я четко опишу эту проблему.

Ответы [ 3 ]

1 голос
/ 06 апреля 2011

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

public void UpdateUser(int userId, Action<User> callback)
{
    using (var db = new DataContext())
    {
        User entity = db.Users.Where(u => u.Id == userId).Single();

        callback(entity);

        db.SubmitChanges();
    }
}

myrepository.UpdateUser(userId, user =>
{
    user.Username = username;
    user.Password = password;
    // etc...
});
1 голос
/ 07 апреля 2011

Добавление другого ответа в качестве комментария недостаточно для расширения моего предыдущего ответа.

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

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

Пример:

// In domain layer:
MyEntity entity = new MyEntity();
entity.PrimaryKey = 10;
entity.Name = "Toby Larone";
entity.Age = 27;

myDataRepository.Update(entity);

// In data layer:
void Update(MyEntity changedEntity)
{
    using (var db = new DataContext())
    {
        var entity = (from e in db.MyEntities
                      where e.PrimaryKey == changedEntity.PrimaryKey
                      select e).First();

        // Linq2Sql now has change tracking of "entity"... any changes made will be persisted when SubmitChanges is called...

        entity = changedEntity;

        // Linq2Sql does **not** have change tracking of changedEntity - the fact that it has been assigned to the same variable that once stored a tracked entity does not mean that Linq2Sql will magically pick up the changes...

        db.SubmitChanges(); // Nothing happens - as far as Linq2Sql is concerned, the entity that was selected in the first query has not been changed (only the variable in this scope has been changed to reference a different entity).
    }
}

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

Одним из возможных решений этой проблемы было бы написание метода, который «применяет» изменения другой сущности к существующей, то есть:

partial class MyEntity
{
    void ApplyChanges(MyEntity changedEntity)
    {
        this.PrimaryKey = changeEntity.PrimaryKey;
        this.Name = changedEntity.Name;
        this.Age = changedEntity.Age;
    }
}

и тогда ваш доступ к данным будет выглядеть так:

// In data layer:
void Update(MyEntity changedEntity)
{
    using (var db = new DataContext())
    {
        var entity = (from e in db.MyEntities
                      where e.PrimaryKey == changedEntity.PrimaryKey
                      select e).First();

        // Linq2Sql now has change tracking of "entity"... any changes made will be persisted when SubmitChanges is called...

        entity.ApplyChanges(changedEntity);

        db.SubmitChanges(); // Works OK...
    }
}

Но я уверен, что вам не понравится это решение - потому что все, что вы сделали, - это фактически переместили повторяющееся назначение полей из хранилища в сам класс Entity ...

Возвращаясь к логической перспективе - все, что вам действительно нужно, это сообщить хранилищу доступа к данным 2 вещи - 1) какую запись вы хотите обновить и 2) каковы изменения. Отправка совершенно новой сущности, которая объединяет эти два требования, не является необходимой для достижения этой цели, на самом деле, я думаю, что это очень неэффективно.

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

* +1025 * Пример:
// In domain layer:
myDataRepository.Update(10, entity =>
{
    entity.Name = "Toby Larone";
    entity.Age = 27;
});

// In data layer:
void Update(int primaryKey, Action<MyEntity> callback)
{
    using (var db = new DataContext())
    {
        var entity = (from e in db.MyEntities
                      where e.PrimaryKey == primaryKey
                      select e).First();

        // Linq2Sql now has change tracking of "entity"... any changes made will be persisted when SubmitChanges is called...

        // The changes that were sent are being applied directly to the Linq2Sql entity, which is already under change tracking...
        callback(entity);

        db.SubmitChanges();
    }
}

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

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

Надеюсь, я объяснил это достаточно хорошо:)

0 голосов
/ 06 апреля 2011

query отличается от типа info.Они могут иметь те же свойства для вас, но код не знает этого.

Теперь, если вы хотите избежать написания ненужного кода, вы можете использовать стороннюю библиотеку, такую ​​как AutoMapper , которая может сделать это за вас.

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