Как заставить LINQ to SQL всегда включать столбец в операторы UPDATE? - PullRequest
1 голос
/ 17 ноября 2010

У меня есть триггер для целей аудита, который требует наличия столбцов аудита в каждом операторе обновления.

Однако LINQ to SQL будет отправлять только те столбцы с ОБНОВЛЕНИЯМ, которые изменились.

Учитывая, что иногда один и тот же пользователь может редактировать столбец (и, следовательно, значение аудита для "UpdatedBy" будет одинаковым) LINQ to SQL будет сталкиваться с ошибками триггера при попытке обновления.

Я копал в Reflectorи обратите внимание, что SubmitChanges в DataContext использует ChangeTracker, но я не могу понять, есть ли какой-нибудь хороший способ убедить LINQ to SQL, что столбец изменился и поэтому должен быть включен.В худшем случае решение будет включать все поля в операторах обновления независимо от изменений.Тем не менее, ссылка ChangeTracker скрыта внутри DataContext и не позволяет нам вводить собственные ChangeTracker.

Я играл с идеей отсоединения и повторного присоединения объекта, но этокажется запутанным в лучшем случае.Если у кого-то есть идея, я буду признателен за это.Приветствия.

1 Ответ

1 голос
/ 17 ноября 2010

Самое простое решение:

Вам нужно будет отключить проверку параллелизма (UpdateCheck.Never) для всех столбцов (вы можете щелкнуть по нему в конструкторе или изменить свой шаблон, если используете шаблоны T4). В качестве альтернативы вам придется скопировать каждое поле в приведенном ниже примере (для этого можно сгенерировать метод шаблона).

В любом случае:

MyDataContext db = new MyDataContext();
MyDataContext db2 = new MyDataContext();
Car betsy = db.Cars.First(c => c.Name == "Betsy");
Car betsy2 = new Car();
betsy2.Id= betsy.Id;
db2.Cars.Attach(betsy2);

/* Could be GetName() or whatever, but allows same */
betsy2.UpdatedBy = betsy.UpdatedBy;  
betsy2.OtherField = "TestTestTest";   
db2.SubmitChanges();

Некоторые другие «решения» ниже. К сожалению, все они включают двойное обновление / что угодно, и не работают, если вы делаете удаление. Поскольку вы проводите аудит, вы, вероятно, делаете резервные копии в другой таблице, куда вы захотите внести исправления в свой триггер, обновляя последнюю запись аудита с помощью '|' когда вы получите свой второй не '|' или что-то (если это всегда в сделке, возможно, не конец света). Хотя все это не идеально.

Грязное решение:

MyDataContext db = new MyDataContext();
Car car = db.Cars.First(c => c.Id == 1);
car.Name = "Betsy";
car.UpdatedBy = String.Format("{0}|{1}", car.UpdatedBy, DateTime.Ticks);
db.SubmitChanges();

car.UpdatedBy = car.UpdatedBy.Substring(0, car.UpdatedBy.LastIndexOf('|'));
db.SubmitChanges();

Немного лучшее решение:

public partial class MyDataContext : DataContext
{
    public override void SubmitChanges(ConflictMode failureMode)
    {
        ChangeSet cs = base.GetChangeSet();
        foreach (object e in cs.Updates.Union(cs.Inserts))
        {
            PropertyInfo updatedBy = e.GetType()
                .GetProperties()
                .FirstOrDefault(p => p.Name == "UpdatedBy");

            if (updatedBy == null)
            {
                base.SubmitChanges(failureMode);
                return;
            }

            string updatedByValue = updatedBy.GetValue(e, null);
            string tempValue = String.Format("{0}|{1}", updatedByValue, DateTime.Ticks;
            updatedBy.SetValue(e, tempValue);
            base.SubmitChanges(failureMode);

            updatedBy.SetValue(e, tempValue.Substring(0, tempValue.LastIndexOf('|')));
            base.SubmitChanges(failureMode);
        }
    }
}

Лучшее решение этого типа, которое я нашел (если вы используете шаблоны T4 для LINQ to SQL, это еще проще):

Либо создайте частичный файл класса, реализующий общий интерфейс для каждого типа проверяемого объекта, либо измените шаблон таким образом, чтобы проверяемые объекты реализовали общий интерфейс, например ::

public interface IAuditable
{
    string UpdatedBy { get; set; }
}

Затем измените ваши SubmitChanges следующим образом:

public partial class MyDataContext : DataContext
{
    public override void SubmitChanges(ConflictMode failureMode)
    {
        ChangeSet cs = base.GetChangeSet();
        foreach (object e in cs.Updates.Union(cs.Inserts))
        {
            if (typeof(IAuditable).IsAssignableFrom(e))
            {
                string tempValue = String.Format("{0}|{1}", ((IAuditable)e).UpdatedBy, DateTime.Ticks);
                ((IAuditable)e).UpdatedBy = tempValue;
                base.SubmitChanges(failureMode);

                ((IAuditable)e).UpdatedBy = tempValue.Substring(0, tempValue.LastIndexOf('|'));
                base.SubmitChanges(failureMode);
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...