我已经为这个问题在互联网上浏览了很长时间,但与覆盖 LINQ 方法有关的结果要少得多。我不确定是否可以完成,但我想知道是否有人可以确认这是否有效,或者提出替代方案。

情况如下(当然这个问题简化了)

我们使用 EF6 Code First 来构建我们的数据库。我们添加了一个自定义(抽象)基类,所有实体都从该基类派生而来。该基类实现了我们用于审计的一些字段(创建日期、创建者、修改日期...),但我们还通过在基类中添加 IsDeleted (bool) 属性来实现软删除系统。

就我们的应用程序而言,永远不能返回带有 IsDeleted == true 的项目。

DataModel如下(再次简化)

Company
    ---> 1 Company has many Departments
    Department
        ---> 1 Department has many Adresses
        Address

过去,我曾尝试通过为 DataContext 中的表(也是一个自定义类,因为它自动处理审计字段)创建“覆盖”来创建消除 IsDeleted 对象的通用检索方法。

对于您在 DataContext 中找到的每个表:
public DbSet<Company> Companies { get; set; }

我们添加了第二个表,只返回未删除的项目。
public IQueryable<Company> _Companies
{
    get { return this.Companies .Where(co => !co.IsDeleted); }
}

所以我们调用 MyDataContext._Companies 而不是 MyDataContext.Companies
这按预期工作。它可以很好地过滤掉已删除的项目。

但是,我们注意到随后的 .Include() 语句并非如此。如果我打电话:
var companies = MyDataContext._Companies.Include(x => x.Departments);

//...

从公司中删除的部门也会返回。

在我们目前的情况下,大部分核心业务逻辑都已经实现了,这些include语句到处都是。它们主要与安全有关。我可以更改所有语句,但我宁愿先寻找一种方法来做到这一点,而不会对现有代码产生太大影响。
这是查询的大小不允许我们单独调用每组实体的第一个应用程序(仅使用直接表而不是包含语句)。

所以我的问题是双重的:
  • 是否可以覆盖 .Include(Func<x,y>) 方法以自动检查所选实体的 IsDeleted 标志?
  • 如果可能的话覆盖,我如何将传递的 lambda 表达式与我想要执行的附加检查结合起来?

  • 所以通过调用
    someTable.Include(x => x.MyRelatedEntity);
    

    它实际上会执行:
    /* Not sure if this is proper syntax. I hope it explains what I'm trying to accomplish. */
    someTable.Include(x => x.MyRelatedEntity.Where(y => !y.IsDeleted));
    

    有人能指出我正确的方向吗?非常感激!

    注意:我知道我的问题中没有太多代码。但我什至不确定我可以在什么级别上实现这一点。如果 Include 不能被覆盖,还有其他方法吗?

    更新

    我实现了建议的解决方案,但遇到了所有数据库调用的问题。错误如下:
    Problem in mapping fragments starting at line 245:Condition member 'Company.IsDeleted' with a condition other than 'IsNull=False' is mapped. Either remove the condition on Company.IsDeleted or remove it from the mapping.
    

    阅读这个问题,似乎如果我使用 IsDeleted 作为条件(即建议的解决方案),我仍然不能将它用作属性。

    那么问题就变成了:如何删除某些内容?一经删除,不得归还。但是一个未删除的项目应该能够被删除。

    有什么方法可以通过 IsDeleted, 过滤返回的项目,但仍然允许将其设置为 true 并保存

    最佳答案

    您正在寻找的解决方案是要求实体具有 IsDeletedfalse 值:

    modelBuilder.Entity<Company>()
        .Map( emc => emc.Requires( "IsDeleted" ).HasValue( false ) );
    

    现在只会从数据库中检索具有 IsDeleted == false 的公司

    从评论更新:
    modelBuilder.Entity<Company>()
        .Map( emc =>
        {
            emc.MapInheritedProperties();
            emc.Requires( "IsDeleted" ).HasValue( false );
        } )
        .Ignore( c => c.IsDeleted );
    

    更新:成功的测试代码(找到帮助方法 here ):
    [Table("EntityA")]
    public partial class EntityA
    {
        public int EntityAId { get; set; }
        public string Description { get; set; }
    
    
        public virtual EntityB PrimaryEntityB { get; set; }
    
        public virtual EntityB AlternativeEntityB { get; set; }
    
        public bool IsDeleted { get; set; }
    }
    
    [Table("EntityB")]
    public partial class EntityB
    {
        public int EntityBId { get; set; }
        public string Description { get; set; }
    
        [InverseProperty("PrimaryEntityB")]
        public virtual ICollection<EntityA> EntityAsViaPrimary { get; set; }
        [InverseProperty( "AlternativeEntityB" )]
        public virtual ICollection<EntityA> EntityAsViaAlternative { get; set; }
    }
    
    public partial class TestEntities : DbContext
    {
        public TestEntities()
            : base("TestEntities")
        {
            Database.SetInitializer( new DatabaseInitializer() );
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<EntityA>()
                .Map( emc =>
                    {
                        emc.Requires( "IsDeleted" ).HasValue( false );
                    } )
                    .Ignore( a => a.IsDeleted );
        }
    
        public override int SaveChanges()
        {
            foreach( var entry in this.ChangeTracker.Entries<EntityA>() )
            {
                if( entry.State == EntityState.Deleted )
                {
                    SoftDelete( entry );
                }
            }
    
            return base.SaveChanges();
        }
    
        private void SoftDelete( DbEntityEntry entry )
        {
            var entityType = entry.Entity.GetType();
    
            var tableName = GetTableName( entityType );
            var pkName = GetPrimaryKeyName( entityType );
    
            var deleteSql = string.Format( "update {0} set IsDeleted = 1 where {1} = @id",
                tableName,
                pkName );
    
            Database.ExecuteSqlCommand( deleteSql, new SqlParameter( "@id", entry.OriginalValues[ pkName ] ) );
    
            entry.State = EntityState.Detached;
        }
    
        private string GetPrimaryKeyName( Type type )
        {
            return GetEntitySet( type ).ElementType.KeyMembers[ 0 ].Name;
        }
    
        private string GetTableName( Type type )
        {
            EntitySetBase es = GetEntitySet( type );
    
            return string.Format( "[{0}].[{1}]",
                es.MetadataProperties[ "Schema" ].Value,
                es.MetadataProperties[ "Table" ].Value );
        }
        private EntitySetBase GetEntitySet( Type type )
        {
            ObjectContext octx = ( ( IObjectContextAdapter )this ).ObjectContext;
    
            string typeName = ObjectContext.GetObjectType( type ).Name;
    
            var es = octx.MetadataWorkspace
                            .GetItemCollection( DataSpace.SSpace )
                            .GetItems<EntityContainer>()
                            .SelectMany( c => c.BaseEntitySets
                                            .Where( e => e.Name == typeName ) )
                            .FirstOrDefault();
    
            if( es == null )
                throw new ArgumentException( "Entity type not found in GetTableName", typeName );
    
            return es;
        }
    
        public DbSet<EntityA> EntityAs { get; set; }
        public DbSet<EntityB> EntityBs { get; set; }
    }
    

    应用代码:
    class Program
    {
        static void Main(string[] args)
        {
            using( var db = new TestEntities() )
            {
                var a0 = new EntityA()
                    {
                        EntityAId = 1,
                        Description = "hi"
                    };
    
                var a1 = new EntityA()
                    {
                        EntityAId = 2,
                        Description = "bye"
                    };
    
                db.EntityAs.Add( a0 );
                db.EntityAs.Add( a1 );
    
                var b = new EntityB()
                {
                    EntityBId = 1,
                    Description = "Big B"
                };
    
                a1.PrimaryEntityB = b;
    
                db.SaveChanges();
    
                // this prints "1"
                Console.WriteLine( b.EntityAsViaPrimary.Count() );
    
                db.EntityAs.Remove( a1 );
    
                db.SaveChanges();
    
                // this prints "0"
                Console.WriteLine( b.EntityAsViaPrimary.Count() );
            }
    
            var input = Console.ReadLine();
        }
    }
    

    关于c# - 覆盖 LINQ .Include(),可能吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21328321/

    10-12 06:11