2013-10-08 55 views
4

我在我的MVC应用程序中使用EF 5数据库的第一种方法。我所有的表都使用一个名为Deleted的字段,这是一个布尔字段来标记记录被删除。EF 5条件映射

我试图摆脱每次查询我的数据库时必须检查Deleted == false的要求。这样做的直接方法是在edmx文件中使用条件映射,其中EF总是返回未被删除的数据。这很好。

但做这种条件映射的问题是,当我想允许用户从他们的地址簿中删除例如地址的某些记录时,我无法从EF中删除字段,因为我在条件映射,因此我不得不寻找另一个选项来允许用户删除一条记录。

我认为的方式是创建一个存储过程,处理删除查询并在我想删除记录时调用它。

有没有更好的方法来做到这一点?即使它在条件映射中使用,是否可以使Delete字段可访问?

回答

14

我有一个工作解决方案Soft Delete in Entity Framework Code First可能有所帮助。

关键是,您可以为您希望能够软删除的每个模型添加一个鉴别器。在第一个代码是这样完成的:

modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));

这使得它无形的背景下,因此您可以使用SQL做删除。

如果这是在数据库中你的“条件映射”的同义的第一,然后修改SQL一个办法就是从那里覆盖的SaveChanges和运行SQL:用于Get Table Name explained here

public override int SaveChanges() 
{ 
    foreach (var entry in ChangeTracker.Entries() 
      .Where(p => p.State == EntityState.Deleted 
      && p.Entity is ModelBase))//I do have a base class for entities with a single 
             //"ID" property - all my entities derive from this, 
             //but you could use ISoftDelete here 
    SoftDelete(entry); 

    return base.SaveChanges(); 
} 

private void SoftDelete(DbEntityEntry entry) 
{ 
    var e = entry.Entity as ModelBase; 
    string tableName = GetTableName(e.GetType()); 
    Database.ExecuteSqlCommand(
      String.Format("UPDATE {0} SET IsDeleted = 1 WHERE ID = @id", tableName) 
      , new SqlParameter("id", e.ID)); 

    //Marking it Unchanged prevents the hard delete 
    //entry.State = EntityState.Unchanged; 
    //So does setting it to Detached: 
    //And that is what EF does when it deletes an item 
    //http://msdn.microsoft.com/en-us/data/jj592676.aspx 
    entry.State = EntityState.Detached; 
} 

方法那是我过去这样做的方式。可能与EF5中的数据库优先方法无关,但我现在已经开始使用存储过程。 EF6 Code First在迁移文件中生成CreateStoredProcedure调用。我将它们替换为this.CreateDeleteProcedure("dbo.Foo_Delete", "[dbo].[Foos]"); - 这是对我自己的扩展方法的调用:

public static class MigrationExtensions 
{ 
    internal static string DeleteSqlFormat 
    { 
     //I also hard delete anything deleted more than a day ago in the same table 
     get { return "DELETE FROM {0} WHERE IsDeleted = 1 AND DATEADD(DAY, 1, DeletedAt) < GETUTCDATE(); UPDATE {0} SET IsDeleted = 1, DeletedAt = GETUTCDATE() WHERE ID = @ID;"; } 
    } 

    internal static void CreateDeleteProcedure(this DbMigration migration, string procName, string tableName) 
    { 
     migration.CreateStoredProcedure(
         procName, 
         p => new 
         { 
          ID = p.Int(), 
         }, 
         body: 

          string.Format(MigrationExtensions.DeleteSqlFormat, tableName) 

        ); 
    } 

} 
+0

+1非常好的解决方案。 –

+0

这就是我一直在寻找的词,“软删除”。是的,这对于代码来说似乎是一个很好的解决方案。很可能,我将在存储过程中创建一个动态查询,并在edmx中使用存储过程映射,并将我创建的存储过程映射到Delete。所以当我在上下文中调用Delete时,它会自动调用我自动映射的Delete存储过程。 – Amila

+0

令人印象深刻的是,在寻找工作解决方案后,我找到了一个。在让GetTableName工作时有一些小问题,你在另一篇文章中发布的版本需要一些小的调整来编译。但这并没有改变这个事实,即这种软删除方法的确像一种魅力。 –