Ok, I've gone completely down the rabbit hole on this one, but I think I have a pretty cool solution:
First, add an event handler to your data context that will collect all of the post-save signals and hide the Dispose
method so that we can call the event right before we dispose. (Note that I use the new
keyword instead of override
. This makes calling the event possible.)
partial class MyDataContext
{
internal delegate void PostSaveHandler();
internal event PostSaveHandler PostSave;
// This method hides the underlying Dispose because we need to call PostSave.
public new void Dispose(bool disposing)
{
// Obviously necessary error handling omitted for brevity's sake
PostSave();
base.Dispose(disposing);
}
}
Next, write a T4 Template that inspects the dbml
file that Linq to Sql generates for you.
<#
var dbml = XDocument.Load(@"MyDataContext.dbml");
var name = XName.Get("Type", "http://schemas.microsoft.com/linqtosql/dbml/2007");
var tables = from t in dbml.Descendants(name) select t.Attribute("Name").Value;
foreach(var table in tables)
{
#>
...
For each table in the database (and thus each partial class), add on to the partial with the following methods.
public partial class Foo
{
internal void OnInsert(MyDataContext db) {
PreInsert();
db.PostSave += delegate { PostInsert(); };
}
internal void OnUpdate(MyDataContext db) {
PreUpdate();
db.PostSave += delegate { PostUpdate(); };
}
internal void OnDelete(MyDataContext db) {
PreDelete();
db.PostSave += delegate { PostDelete(); };
}
partial void PreInsert();
partial void PostInsert();
partial void PreUpdate();
partial void PostUpdate();
partial void PreDelete();
partial void PostDelete();
}
// repeat for all tables
Also add another partial MyDataContext
via T4. This will be adding definitions to the partial methods that Linq to SQL gives you (as Merritt mentioned).
public partial class MyDataContext
{
// Add these three partial methods for each table
partial void InsertFoo(Foo foo)
{
foo.OnInsert(this);
ExecuteDynamicInsert(foo);
}
partial void UpdateFoo(Foo foo)
{
foo.OnUpdate(this);
ExecuteDynamicUpdate(foo);
}
partial void DeleteFoo(Foo foo)
{
foo.OnDelete(this);
ExecuteDynamicDelete(foo);
}
// ...
}
Hide those files away somewhere safe, so no one tries to mess with them.
Your signals framework is set up. Now you can write your signals. Put these either in Foo.cs
or all together in a Signals.cs
file:
partial class Foo
{
partial void PostInsert()
{
EventLog.AddEvent(EventType.FooInserted, this);
}
}
This is a bit complex, so if anything doesn't make sense, please leave a comment and I'll do my best to address it.