Fla*_*ter 5 c# linq linq-to-entities entity-framework
我已经在互联网上浏览这个问题相当长一段时间了,但我得到的与重写 LINQ 方法有关的结果要少得多。我不确定是否可以做到,但我想知道是否有人可以确认这是否有效,或者提出替代方案。
情况如下(当然针对这个问题进行了简化)
我们使用 EF6 Code First 来构建我们的数据库。我们添加了一个自定义(抽象)基类,所有实体均从中派生。该基类实现了我们用于审核的一些字段(创建日期、创建者、修改日期……),但我们还通过在基类中添加IsDeleted (bool) 属性来实现软删除系统。
据我们的应用程序所知,IsDeleted绝不能返回带有 == true 的项。
DataModel 如下(再次简化)
Company
---> 1 Company has many Departments
Department
---> 1 Department has many Adresses
Address
Run Code Online (Sandbox Code Playgroud)
过去,我尝试创建一个通用的检索方法,通过创建对 DataContext 中的表的“覆盖”(也是一个自定义类,因为它自动处理审核字段)来消除 IsDeleted 对象。
对于在 DataContext 中找到的每个表:
public DbSet<Company> Companies { get; set; }
Run Code Online (Sandbox Code Playgroud)
我们添加了第二个表,仅返回未删除的项目。
public IQueryable<Company> _Companies
{
get { return this.Companies .Where(co => !co.IsDeleted); }
}
Run Code Online (Sandbox Code Playgroud)
所以我们调用MyDataContext._Companies而不是MyDataContext.Companies. 这按预期工作。它很好地过滤掉了已删除的项目。
然而,我们注意到随后的声明却并非如此.Include()。如果我打电话:
var companies = MyDataContext._Companies.Include(x => x.Departments);
//...
Run Code Online (Sandbox Code Playgroud)
从公司中删除的部门也会返回。
就我们现在的情况来说,大部分核心业务逻辑已经实现了,这些include语句也到处都是。它们主要与安全有关。我可以更改所有语句,但我宁愿首先寻找一种方法来执行此操作,而又不会对现有代码产生太大影响。
这是第一个应用程序,其中查询的大小不允许我们单独调用每组实体(通过仅使用直接表而不是包含语句)。
所以我的问题是双重的:
.Include(Func<x,y>)检查吗?IsDeleted所以通过调用
someTable.Include(x => x.MyRelatedEntity);
Run Code Online (Sandbox Code Playgroud)
它实际上会执行:
/* 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));
Run Code Online (Sandbox Code Playgroud)
有人能指出我正确的方向吗?非常感激!
注意:我知道我的问题中没有太多代码。但我什至不确定我可以在什么级别上实现这一点。如果 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.
Run Code Online (Sandbox Code Playgroud)
阅读这个问题,似乎如果我用作IsDeleted条件(即建议的解决方案),我仍然不能将它用作属性。
那么问题就变成了:如何删除某些内容?一旦删除,就不应归还。但未删除的项目应该能够被删除。
有什么方法可以通过 IsDeleted 过滤返回的项目,但仍然允许将其设置为 true 并保存它?
您正在寻找的解决方案是要求实体IsDeleted的值为false:
modelBuilder.Entity<Company>()
.Map( emc => emc.Requires( "IsDeleted" ).HasValue( false ) );
Run Code Online (Sandbox Code Playgroud)
现在只有包含的公司IsDeleted == false才会从数据库中检索
评论更新:
modelBuilder.Entity<Company>()
.Map( emc =>
{
emc.MapInheritedProperties();
emc.Requires( "IsDeleted" ).HasValue( false );
} )
.Ignore( c => c.IsDeleted );
Run Code Online (Sandbox Code Playgroud)
更新:测试代码成功(此处找到帮助器方法):
[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; }
}
Run Code Online (Sandbox Code Playgroud)
申请代码:
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();
}
}
Run Code Online (Sandbox Code Playgroud)