2009-06-17 12 views
9

¿Alguien sabe de una manera de hacer algo similar a Django's signals usando LINQ to SQL?Señales en Linq a Sql?

Estoy tratando de registrar cuándo se insertan nuevas filas y cuándo se actualizan ciertas columnas, así que realmente solo quiero las señales pre_save y post_save.

que pueda especie de hacerlo con algunos modelos mediante el uso de los parciales definidos como OnFooIDChanging() y OnFooIDChanged() (donde FooID es una clave primaria), pero esto no funciona para los modelos cuya clave principal no es una identidad, o si se establece por código

Para aquellos, que posiblemente podría utilizar OnValidate(), pero que sólo sería pre_save, y hace frente a la base de datos duros, ya que OnValidate() se llama desde DBContext.SubmitChanges(), que por supuesto no se deja un segundo SubmitChanges() a llamarse desde dentro, por lo que post_save es básicamente imposible por lo que puedo ver.

Respuesta

1

Ok, me he vuelto completamente el agujero del conejo en este caso, pero creo que tengo una solución muy bien:

En primer lugar, añadir un controlador de eventos a su contexto de datos que recogerá todo del puesto -guarde las señales y oculte el método Dispose para que podamos llamar al evento justo antes de desecharlo. (Nótese que uso la palabra clave en lugar de newoverride. Esto hace posible llamar al evento.)

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); 
    } 
} 

A continuación, escribir una T4 Template que inspecciona el archivo dbml que LINQ to SQL genera para usted.

<# 
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) 
{ 
#> 
    ... 

Para cada tabla en la base de datos (y por lo tanto cada clase parcial), agregue al parcial con los siguientes métodos.

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 

También añada otro partial MyDataContext vía T4. Esto agregará definiciones a los métodos parciales que Linq to SQL le brinda (como mencionó Merritt).

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); 
    } 

    // ... 
} 

Oculte esos archivos en un lugar seguro, para que nadie intente meterse con ellos.

Su marco de señales está configurado. Ahora puedes escribir tus señales. Poner éstos ya sea en Foo.cs o todos juntos en un archivo Signals.cs:

partial class Foo 
{ 
    partial void PostInsert() 
    { 
     EventLog.AddEvent(EventType.FooInserted, this); 
    } 
} 

Esto es un poco complejo, por lo que si algo no tiene sentido, por favor deje un comentario y haré mi mejor esfuerzo para resolver el problema.

1

tengo una solución mucho más fácil de lo que ya publicado, que no funcionaba de todos modos: SubmitChanges de anulación (en ConflictMode failureMode):

partial class MyDataContext 
{ 
    // SubmitChanges() calls this method after inserting default value for param 
    public override void SubmitChanges(ConflictMode failureMode) 
    { 

      // Pre-Submit Changes 

      //Updates    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Updates.Count; changeCounter++) 
      {     
       var modifiedEntity = this.GetChangeSet().Updates[changeCounter];     
       // Do something, for example: 
       // var tableXEntry = new TableX() { Prop1 = "foo" }; 
       // this.tableXEntries.InsertOnSubmit(tableXEntry); 
      }    

      //Inserts    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Inserts.Count; changeCounter++)    
      {     
       object modifiedEntity = this.GetChangeSet().Inserts[changeCounter];     
       // Do Something 
      } 


      // Submit Changes 
      base.SubmitChanges(failureMode); 


      // Post Submit Changes 

      //Updates    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Updates.Count; changeCounter++) 
      {     
       var modifiedEntity = this.GetChangeSet().Updates[changeCounter];     
       // Do something, for example: 
       // var tableXEntry = new TableX() { Prop1 = "foo" }; 
       // this.tableXEntries.InsertOnSubmit(tableXEntry); 
      }    

      //Inserts    
      for (int changeCounter = 0; changeCounter < this.GetChangeSet().Inserts.Count; changeCounter++)    
      {     
       object modifiedEntity = this.GetChangeSet().Inserts[changeCounter];     
       // Do Something 
      } 
} 

Con el marco de la entidad, hago algo similar a lo que está tratando hacer: después de guardar una entidad, inserto una nueva entrada en una tabla diferente para propósitos de auditoría (es una copia de la entidad antes de los cambios). hay un evento SaveChanges() en el contenedor de entidades EF (como el contexto de datos) que le permite agregar al contexto actual antes de guardar los cambios.