如何在C# LINQ中创建泛型Join()

xfyts7mz  于 2022-12-25  发布在  C#
关注(0)|答案(2)|浏览(145)

我需要编写一个泛型连接()函数执行两个数据库集之间的查询,这两个数据库集是TEntity类型的实体和TParent类型的parentEntities。实体的PK和父实体的名称的每个对象。两种类型都有IBaseEntity接口,因此Id列可用,但我需要一种在实体中通用指定外键列的方法(示例中的fkCol)和parentEntities名称列(parentNameCol)。

public static IQueryable<cObjectNames> Join<TEntity, TParent>(IQueryable<TEntity> entities, IQueryable<TParent> parenEntities,
    string fkCol, string parentNameCol)
    where TEntity : class, IBaseEntity where TParent : class, IBaseEntity
{
    IQueryable<cObjectNames> qNames = entities.Join(parenEntities, e => e.fkCol, p => p.Id, (e, p) =>
        new cObjectNames() { name = p.parentNameCol, eId = e.Id });

    return qNames;
}

我知道可以使用EF来获取父对象,但是我需要一个通用的解决方案来解决几个这样的fk关系,其中甚至父名称列也不是常量。请保存动态LINQ的建议- LINQ通用表达式要酷得多...
cObjectNames的定义为

public class cObjectNames 
{
    public int eId{ get; set; }
    public string name{ get; set; }
}

IBaseEntity接口为:

public interface IBaseEntity
{
    int Id { get; set; }
    DateTimeOffset Created { get; set; }
    DateTimeOffset? Lastupdated { get; set; }
    DateTimeOffset? Deleted { get; set; }
}

谢谢!

aoyhnmkz

aoyhnmkz1#

下面是实现。我希望内联注解有用。

public static class JoinExtensions
{
    public static IQueryable<cObjectNames> Join<TEntity, TParent>(this IQueryable<TEntity> entities, IQueryable<TParent> parentEntities,
        string fkCol, string parentNameCol)
        where TEntity : class, IBaseEntity where TParent : class, IBaseEntity
    {
        // we can reuse this lambda and force compiler to do that
        Expression<Func<TEntity, int>> entityKeySelector = e => e.Id;
        Expression<Func<TParent, int>> parentKeySelector = p => p.Id;

        var entityParam = entityKeySelector.Parameters[0];
        var parentParam = parentKeySelector.Parameters[0];

        // Ensure types are correct
        var fkColExpression = (Expression)Expression.Property(entityParam, fkCol);
        if (fkColExpression.Type != typeof(int))
            fkColExpression = Expression.Convert(fkColExpression, typeof(int));

        // e => e.fkCol
        var fkColSelector = Expression.Lambda(fkColExpression, entityParam);
        
        // (e, p) => new cObjectNames { name = p.parentNameCol, eId = e.Id }
        var resultSelector = Expression.Lambda(Expression.MemberInit(Expression.New(cObjectNamesConstrtuctor),
                Expression.Bind(cObjectNamesNameProp,
                    Expression.Property(parentParam, parentNameCol)),
                Expression.Bind(cObjectNamesIdProp,
                    entityKeySelector.Body)),
            entityParam, parentParam);

        //  full Join call
        var queryExpr = Expression.Call(typeof(Queryable), nameof(Queryable.Join),
            new Type[] { typeof(TEntity), typeof(TParent), typeof(int), typeof(cObjectNames) },
            entities.Expression,
            parentEntities.Expression,
            Expression.Quote(fkColSelector),
            Expression.Quote(parentKeySelector),
            Expression.Quote(resultSelector)
        );

        var qNames = entities.Provider.CreateQuery<cObjectNames>(queryExpr);

        return qNames;
    }

    static ConstructorInfo cObjectNamesConstrtuctor = typeof(cObjectNames).GetConstructor(Type.EmptyTypes) ??
                                                        throw new InvalidOperationException();

    static MemberInfo cObjectNamesNameProp = typeof(cObjectNames).GetProperty(nameof(cObjectNames.name)) ??
                                                throw new InvalidOperationException();
    static MemberInfo cObjectNamesIdProp = typeof(cObjectNames).GetProperty(nameof(cObjectNames.eId)) ??
                                                throw new InvalidOperationException();
}
lsmepo6l

lsmepo6l2#

您可以从EF-Core上下文中检索元数据:

IEntityType entityType = context.Model
    .FindEntityTypes(typeof(TEntity))
    .FirstOrDefault();

从这里您可以获得导航属性:

foreach (IReadOnlyNavigation nav in entityType.GetNavigations()) {
    if (nav.IsOnDependent) {
        var parentProp = nav.Inverse?.PropertyInfo;
        var childProp = nav.PropertyInfo;
        Type parentType = nav.TargetEntityType.ClrType;
        var foreignKeyProps = nav.ForeignKey.Properties;
        ... etc., etc.
    }
}

至少,这是一个起点。然后,您必须通过反射创建表达式。请参阅:How do I dynamically create an Expression<Func<MyClass, bool>> predicate from Expression<Func<MyClass, string>>?.

相关问题