首页 > 解决方案 > 希望将实体作为通用类型传递并提取通用功能

问题描述

我有一个如下所示的实体结构,我在这里所做的是基于输入找到主实体,然后通过导航属性找到子实体,然后从不同位置的子表中删除子实体

public class GlazingOrGasMaterial : AEIMasterBase
{
    [ForeignKey("SourceOfData"), GraphQLIgnore]
    public Guid? SourceOfDataId { get; set; }
    public virtual CodeStandardGuideline SourceOfData { get; set; }
    [ForeignKey("GlazingComplexMaterial"), GraphQLIgnore]
    public Guid? GlazingComplexMaterialId { get; set; }
    [ForeignKey("GlazingSimpleMaterial"), GraphQLIgnore]
    public Guid? GlazingSimpleMaterialId { get; set; }
    [ForeignKey("GlazingGasMaterial"), GraphQLIgnore]
    public Guid? GlazingGasMaterialId { get; set; }
    public virtual GlazingComplexMaterial GlazingComplexMaterial { get; set; }
    public virtual GlazingSimpleMaterial GlazingSimpleMaterial { get; set; }
    public virtual GlazingGasMaterial GlazingGasMaterial { get; set; }
}

以下是删除该子实体的代码

方法一:

   if(requestInput.RequestType == RequestAction.EDIT)
    {
        var existingGlazingOrGasMaterial = dbContext.GlazingOrGasMaterials.Find(requestInput.DataId);
        if(existingGlazingOrGasMaterial != default)
        {
            var existingGasMaterial = dbContext.GlazingGasMaterials.SingleOrDefault(gasMaterial => gasMaterial.Id == existingGlazingOrGasMaterial.GlazingGasMaterialId);
            if(existingGasMaterial != default)
            {
                dbContext.GlazingGasMaterials.Remove(existingGasMaterial);
            }
        }
    }

下面是我删除该子实体的代码

方法二:

    if (requestInput.RequestType == RequestAction.EDIT)
    {
        var existingGlazingOrGasMaterial = dbContext.GlazingOrGasMaterials.Find(requestInput.DataId);
        if (existingGlazingOrGasMaterial != default)
        {
            var existingComplexMaterial = dbContext.GlazingComplexMaterials.SingleOrDefault(complexMaterial => complexMaterial.Id == existingGlazingOrGasMaterial.GlazingComplexMaterialId);
            if (existingComplexMaterial != default)
            {
                dbContext.GlazingComplexMaterials.Remove(existingComplexMaterial);
            }
        }
    }

方法三:

    if (requestInput.RequestType == RequestAction.EDIT)
    {
        var existingGlazingOrGasMaterial = dbContext.GlazingOrGasMaterials.Find(requestInput.DataId);
        if (existingGlazingOrGasMaterial != default)
        {
            var existingSimpleMaterial = dbContext.GlazingSimpleMaterials.SingleOrDefault(simpleMaterial => simpleMaterial.Id == existingGlazingOrGasMaterial.GlazingSimpleMaterialId);
            if (existingSimpleMaterial != default)
            {
                dbContext.GlazingSimpleMaterials.Remove(existingSimpleMaterial);
            }
        }
    }

下面是子实体的结构

public class GlazingSimpleMaterial 
{
    public Guid Id { get; set; }
    .....
    .....
}
public class GlazingComplexMaterial 
{
    public Guid Id { get; set; }
    .....
    .....
}
public class GlazingGasMaterial 
{
    public Guid Id { get; set; }
    .....
    .....
}

我正在寻找一种通用方法,我们可以将实体作为泛型类型传递并进行删除,有人可以告诉我如何实现这一点吗?

提前谢谢了!!

标签: c#.netlinqgenericsentity-framework-core

解决方案


然后通过导航属性找到子实体

您无需手动执行此操作,EF Core 可以使用加载相关数据的可用方法为您执行此操作- 急切、显式或惰性。在这种特殊情况下,预加载可能是最合适的,因为它允许您将前两个步骤(查找主实体和关联的子实体)与单个数据库请求结合在一起。

无论您选择哪种查找子实体的方法,所讨论的方法的概括都是提供导航属性访问器表达式 ( Expression<Func<TParent, TChild>>),这足以执行当前代码的等效操作。

例如,假设GlazingOrGasMaterial.Id属性类型是Guid并且来自AEIMasterBase类,主体可以概括为

public static void RemoveChild<TParent, TChild>(
    DbContext db,
    Guid parentId,
    Expression<Func<TParent, TChild>> childReference // <--
)
    where TParent : AEIMasterBase
    where TChild : class
{
    var parent = db.Set<TParent>()
        .Include(childReference) // <--
        .FirstOrDefault(p => p.Id == parentId);
    if (parent != null)
    {
        var child = db.Entry(parent)
            .Reference(childReference) // <--
            .CurrentValue;
        if (child != null)
            db.Remove(child);
    }
}

但是现在可以很容易地看出您并不真的需要父实体,因此可以将实现简化(也变得更高效)如下(相同的方法签名):

var child = db.Set<TParent>()
    .Where(p => p.Id == parentId)
    .Select(childReference) // <--
    .FirstOrDefault();
if (child != null)
    db.Remove(child);

在这两种情况下,这 3 个电话都是

if (requestInput.RequestType == RequestAction.EDIT)
    RemoveChild(dbContext, requestInput.DataId, (GlazingOrGasMaterial m) => m.{NavigationProperty});

m.{NavigationProperty}分别m.GlazingGasMaterial在哪里m.GlazingComplexMaterialm.GlazingSimpleMaterial


推荐阅读