Easiest solution:
You'll have to disable concurrency checking (UpdateCheck.Never) on all columns (you can shift click it in the designer, or modify your template if using T4 templates). Alternatively you'll have to copy every field in the example below (could generate a template method to do this).
In any case:
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();
Some other "solutions" below. Unfortunately all of these involve a double update/whatever, and don't really work if you're doing a delete. As you're auditing is probably backing up to another table somewhere you're going to want to patch in your trigger around updating the last audit entry with '|' when you get your second non '|' or something (if it's always in a transaction perhaps not the end of the world). It's all non-ideal though.
The messy solution:
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();
The slightly better solution:
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);
}
}
}
The best solution of this type I've found (if you're using the T4 Templates for LINQ to SQL this is even easier):
Either make a partial class file implementing a common interface for each audited entity type or modify the template so that audited entities implement a common interface, e.g.:
public interface IAuditable
{
string UpdatedBy { get; set; }
}
Then modify your SubmitChanges as follows:
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);
}
}
}
}