在EF6扩展DBContext类中的运行时间创建一个联接



我需要在运行时创建一个表加入,并给定配置。

在这种情况下,我具有可视化属性,它是根,我需要动态创建加入。

这里我尝试过的东西。

public class MyDbContext : DbContext
{
    public IQueryable<T> AsDynamicQueryable<T>() where T : class
    {
        var predicate = default(Func<T, bool>); // This is a Dynamically generated predicate 
        var query = this.Set<T>().Where(predicate).AsQueryable();

        // Now here I need to append a JOIN to the above 'query'
        // So far, this is what I have done.
        var rootType = typeof(T);
        var innerType = Type.GetType("This type takes from the configuration");
        var innerExpression = this.Set(innerType).AsQueryable();
        var paramOne = Expression.Parameter(rootType, "p1");
        var paramTwo = Expression.Parameter(innerType, "p2");
        var outerKeySelector = Expression.Property(paramOne, "property_one"); //'property_one' is a property of a first parameter which takes from the configuration
        var outerKeySelectorExpression = Expression.Lambda(outerKeySelector, paramOne); // (p1)=>p1.property_one
        var innerKeySelector = Expression.Property(paramTwo, "property_two"); //'property_two' is a property of a 2nd parameter which takes from the configuration
        var innerKeySelectorExpression = Expression.Lambda(innerKeySelector, paramTwo); // (p2)=>p2.property_two
        var resultSelector = Expression.Lambda(paramOne, paramOne, paramTwo); // (p1,p2)=>p1
        var joinMethod = typeof(Queryable)
                            .GetMethods()
                            .First(m => m.Name == "Join" && m.GetParameters().Length == 5)
                            .MakeGenericMethod(rootType, innerType, typeof(int), rootType);

        // 1st Apptempt. 
        // I'm not sure that I can execute the JOIN method like this.
        // But anyway, this gives the below error when I try to execute via taking Count();
        // "This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code."
        var newQuery = (IQueryable<T>)joinMethod
                                        .Invoke(
                                            query,
                                            new object[]
                                                { 
                                                    query,
                                                    innerExpression,
                                                    outerKeySelectorExpression,
                                                    innerKeySelectorExpression,
                                                    resultSelector
                                                });
        var tt = newQuery.Count(); // Here I just try to execute the expression to check whether it works before I return the Queryable.

        // 2nd Attempt
        // This also gives the following error when I try to execute via taking Count();
        // Unable to create a constant value of type '<type name of the root(T) type>'. Only primitive types or enumeration types are supported in this context.
        var joinMethodCallExpression = Expression.Call(
                                                    null,
                                                    joinMethod,
                                                    query.Expression,
                                                    innerExpression.Expression,
                                                    outerKeySelectorExpression,
                                                    innerKeySelectorExpression,
                                                    resultSelector);
        var xx = this.Set<T>().AsQueryable().Provider.CreateQuery<T>(joinMethodCallExpression);
        var te = xx.Count(); // Here I just try to execute the expression to check whether it works before I return the Queryable.
        throw new NotImplementedException();
    }
}

如果有人可以指出正确的方法。

这是代码。我在代码中添加了评论:

public IQueryable<T> AsDynamicQueryable<T>() where T : class
{
    // ERROR!!! It should be Expression<Func<T, bool>>
    // GetPredicate<T>() is my method to get the predicate. You must
    // put here yours. IT must return an Expression<Func<T, bool>>
    Expression<Func<T, bool>> predicate = GetPredicate<T>(); // This is a Dynamically generated predicate 
    // ERROR!!! Don't EVER use AsQueryable(), unless you exactly know
    // what you are doing. In this example, your use of AsQueryable<>()
    // is hiding the fact that you are executing the Where() LOCALLY,
    // because it is a Where(this IEnumerable<>, Func<>) instead of
    // being a Where(this IQueryable<>, Expression<>)
    // If you want an IQueryable<>, put it in a IQueryable<> variable
    IQueryable<T> query = this.Set<T>().Where(predicate);
    var rootType = typeof(T);
    var innerType = GetAsDynamicQueryableInnerType<T>();
    // Same as before! Don't use .AsQueryable(). In this case, use 
    // IQueryable (non-generic). Note that in this case there was
    // no problem with yoru code, so AsQueryable() wasn't doing 
    // "damage"
    IQueryable innerExpression = this.Set(innerType);
    var paramOne = Expression.Parameter(rootType, "p1");
    var paramTwo = Expression.Parameter(innerType, "p2");
    // GetPrimaryKey() is my method to get the property to use.
    // it returns a string with the name of the property
    string primaryKeyRootType = GetPrimaryKey(rootType);
    var outerKeySelector = Expression.Property(paramOne, primaryKeyRootType); //'property_one' is a property of a first parameter which takes from the configuration
    var outerKeySelectorExpression = Expression.Lambda(outerKeySelector, paramOne); // (p1)=>p1.property_one
    // GetForeignKey() is my method to get the property to use.
    // it returns a string with the name of the property
    var foreignKeyInnerType = GetForeignKey(innerType, rootType);
    var innerKeySelector = Expression.Property(paramTwo, foreignKeyInnerType); //'property_two' is a property of a 2nd parameter which takes from the configuration
    var innerKeySelectorExpression = Expression.Lambda(innerKeySelector, paramTwo); // (p2)=>p2.property_two
    var resultSelector = Expression.Lambda(paramOne, paramOne, paramTwo); // (p1,p2)=>p1
    // Using outerKeySelector.Type as the type of the third parameter
    // here. 99% it is typeof(int), but why not make it more generic?
    var joinMethod = typeof(Queryable)
                        .GetMethods()
                        .First(m => m.Name == "Join" && m.GetParameters().Length == 5)
                        .MakeGenericMethod(rootType, innerType, outerKeySelector.Type, rootType);

    // Queryable.Join is static, so the first parameter must be null!
    // Then the parameters to pass to Queryable.Join are the ones you
    // where using in the 1st case.
    var newQuery = (IQueryable<T>)joinMethod.Invoke(
                                        null,
                                        new object[]
                                        { 
                                            query,
                                            innerExpression,
                                            outerKeySelectorExpression,
                                            innerKeySelectorExpression,
                                            resultSelector
                                        });
    return newQuery;
}

然后是一个大问题:即使它可以起作用,您也只能恢复IQueryable<T>,但是联接的结果通常是IQueryable<T+U>。我看到您写了resultSelector = ... (p1,p2)=>p1,但这真的是您想要的吗?

最新更新