2016-01-21 23 views
8

ho implementato un database morbido eliminare (un flag booleano che segna le voci come cancellato) utilizzando il seguente tutorial: http://www.codeguru.com/csharp/csharp/soft-deleting-entities-cleanly-using-entity-framework-6-interceptors.htmlEntity Framework morbido eliminare implementazione utilizzando database di intercettore non funziona

Mi sembra una buona implementazione, perché una volta l'impostazione di soft delete viene applicata a un modello semplicemente aggiungendo un'annotazione [SoftDelete("IsDeleted")]. Il problema è così lontano che non funziona.

La fonte sembra essere affidabili, e hanno anche pubblicato un esempio della loro soluzione: https://github.com/rakeshbabuparuchuri/EFExpensionPoints

Si può avere uno sguardo al mio codice nel caso in cui ho fatto qualcosa di sbagliato quando si applica il morbido di eliminazione per il mio progetto?

Questo è il modello:

[SoftDelete("IsDeleted")] 
public class BC_Instance 
{ 
    public int ID { get; set; } 
    public bool IsDeleted { get; set; } 
} 

ApplicationDbContext.cs:

namespace bcplatform2.Models 
{ 
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
    { 
     public ApplicationDbContext() 
      : base("DefaultConnection", throwIfV1Schema: false) 
     { 
     } 

     // Add a DbSet for each one of your Entities 
     //public DbSet<VirtualGuest> VirtualGuests { get; set; } 
     public DbSet<BC_Instance> BiocloudInstances { get; set; } 

     static ApplicationDbContext() 
     { 
      Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer()); 
     } 

     public static ApplicationDbContext Create() 
     { 
      return new ApplicationDbContext(); 
     } 

     protected new void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      var conv = new AttributeToTableAnnotationConvention<SoftDeleteAttribute, string>(
       "SoftDeleteColumnName", 
       (type, attributes) => attributes.Single().ColumnName); 

      modelBuilder.Conventions.Add(conv); 
     } 
    } 
} 

ApplicationDbConfiguration.cs

SoftDeleteAttribute.cs:

namespace bcplatform2.Helpers 
{ 
    public class SoftDeleteAttribute : Attribute 
    { 
     public SoftDeleteAttribute(string column) 
     { 
      ColumnName = column; 
     } 

     public string ColumnName { get; set; } 

     public static string GetSoftDeleteColumnName(EdmType type) 
     { 
      MetadataProperty annotation = type.MetadataProperties 
       .Where(p => p.Name.EndsWith("customannotation:SoftDeleteColumnName")) 
       .SingleOrDefault(); 

      return annotation == null ? null : (string)annotation.Value; 
     } 
    } 
} 

SoftDeleteInterceptor.cs

ho notato che SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType) non trova l'attributo morbido di eliminazione e restituisce null. Ma non so perché.

namespace bcplatform2.Helpers 
{ 
    public class SoftDeleteInterceptor : IDbCommandTreeInterceptor 
    { 
     public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) 
     { 
      if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace) 
      { 
       var queryCommand = interceptionContext.Result as DbQueryCommandTree; 
       if (queryCommand != null) 
       { 
        var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor()); 
        interceptionContext.Result = new DbQueryCommandTree(
         queryCommand.MetadataWorkspace, 
         queryCommand.DataSpace, 
         newQuery); 
       } 

       var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree; 
       if (deleteCommand != null) 
       { 
        var column = SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType); 
        if (column != null) 
        { 
         var setClauses = new List<DbModificationClause>(); 
         var table = (EntityType)deleteCommand.Target.VariableType.EdmType; 
         if (table.Properties.Any(p => p.Name == column)) 
         { 
          setClauses.Add(DbExpressionBuilder.SetClause(
            DbExpressionBuilder.Property(
             DbExpressionBuilder.Variable(deleteCommand.Target.VariableType, deleteCommand.Target.VariableName), 
             column), 
            DbExpression.FromBoolean(true))); 
         } 

         var update = new DbUpdateCommandTree(
          deleteCommand.MetadataWorkspace, 
          deleteCommand.DataSpace, 
          deleteCommand.Target, 
          deleteCommand.Predicate, 
          setClauses.AsReadOnly(), 
          null); 

         interceptionContext.Result = update; 
        } 
       } 
      } 
     } 
    } 
} 

IdentityConfig.cs

public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext> 
{ 
    protected override void Seed(ApplicationDbContext context) 
    { 
     InitializeIdentityForEF(context); 
     base.Seed(context); 
    } 

    //Create [email protected] with [email protected] in the Admin role   
    public static void InitializeIdentityForEF(ApplicationDbContext db) 
    { 
     //Initialize users and roles... 
    } 
} 

risposta

7

C'è un bug in ApplicationDbContext.cs:

protected new void OnModelCreating(DbModelBuilder modelBuilder) {...} 

Si utilizza "nuovo" al posto di "override" in modo OnModelCreating non viene mai eseguito (tenta di aggiungere un punto di interruzione per controllare). Quindi AttributeToTableAnnotationConvention non viene mai eseguito e l'annotazione dell'entità non viene mai aggiunta.

Cambiare a

protected override void OnModelCreating(DbModelBuilder modelBuilder) {...} 

sarà farlo funzionare

+0

Se eseguo l'override, l'OnModelCreating originale non verrà chiamato e non dovrebbe essere il caso. 'new' d'altra parte fa chiamare entrambi i metodi. – nest

+0

Non è corretto. È necessario eseguire l'override e quindi chiamare base.OnModelCreating per delegare al metodo predefinito (se lo si desidera). Basta impostare un punto di interruzione nel metodo corrente e verificare se si ferma, vedrai che non lo farà. – tede24

+0

@ tede24 ha ragione! –

1

Beh, il codice sembra che vada bene per me. Forse c'è un piccolo errore che sta rompendo la tua app. Si potrebbe provare questo:

  1. Rimuovere il SoftDeleteAttribute da BC_Instance

  2. Modificare il metodo OnModelCreating

    AttributeToTableAnnotationConvention<SoftDeleteAttribute, string> conv = 
        new AttributeToTableAnnotationConvention<SoftDeleteAttribute, string>(
         "SoftDeleteColumnName", 
         (type, attributes) => attributes.Single().ColumnName); 
    
    modelBuilder.Conventions.Add(conv); 
    //this will dynamically add the attribute to all models 
    modelBuilder.Types().Configure(delegate(ConventionTypeConfiguration i) 
    { 
        i.HasTableAnnotation("SoftDeleteColumnName", Entity.G etSoftDeleteColumnName()); 
    }); 
    
  3. Elimina ApplicationDbConfiguration classe

  4. Modificare il costruttore di contesto

    public ApplicationDbContext() 
        : base("DefaultConnection", throwIfV1Schema: false) 
    { 
        DbInterception.Add(new SoftDeleteInterceptor()); 
    } 
    

Spero che questo aiuti!

+0

Grazie per il vostro aiuto. Un problema: in linea 'i.HasTableAnnotation (" SoftDeleteColumnName ", Entity.GetSoftDeleteColumnName()); '->' Il nome 'Entità non esiste nel contex corrente'. – nest

+0

Inoltre, come dovrei controllare se funziona. Se interrogo direttamente il database dovrei vedere i record deleter giusto? L'intercettatore viene applicato solo sul codice? – nest

+0

Nel mio caso, Entity è una classe base che viene ereditata dai modelli –