سؤال

أحاول الجمع بين التعبيرات التالية في تعبير واحد: العنصر => Item.SUB، SUB => SUB.KEY لتصبح العنصر => Item.Sub.Key. أحتاج إلى القيام بذلك حتى أتمكن من إنشاء طريقة طلبية تأخذ محدد العنصر بشكل منفصل إلى محدد المفتاح. يمكن تحقيق ذلك باستخدام إحدى الحمل الزائد في ترتيب وتقديم IComparer<T>, ، ولكن لن يترجم إلى SQL.

فيما يلي توقيع طريقة لتوضيح ما أحاول التحول، إلى جانب تنفيذ لا يعمل، ولكن يجب أن يوضح النقطة.

    public static IOrderedQueryable<TEntity> OrderBy<TEntity, TSubEntity, TKey>(
        this IQueryable<TEntity> source, 
        Expression<Func<TEntity, TSubEntity>> selectItem, 
        Expression<Func<TSubEntity, TKey>> selectKey)
        where TEntity : class
        where TSubEntity : class 
    {
        var parameterItem = Expression.Parameter(typeof(TEntity), "item");
        ...
        some magic
        ...
        var selector = Expression.Lambda(magic, parameterItem);
        return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderBy", new Type[] { source.ElementType, selector.Body.Type },
                 source.Expression, selector
                 ));
    } 

والتي سوف تسمى على النحو التالي:

.OrderBy(item => item.Sub, sub => sub.Key)

هل هذا ممكن؟ هل هناك طريقة أفضل؟ السبب في أنني أريد طريقة ترتيب النظام التي تعمل بهذه الطريقة هي دعم تعبير اختيار رئيسي معقد ينطبق على العديد من الكيانات، على الرغم من أنهم يتعرضون بطرق مختلفة. أيضا، أنا أدرك طريقة للقيام بذلك باستخدام تمثيلات سلسلة من الخصائص العميقة، لكنني أحاول إبقائها مكتوبة بشدة.

هل كانت مفيدة؟

المحلول

لأن هذا هو LinQ-To-SQL، يمكنك عادة استخدام Expression.Invoke لإحضار التعبير الفرعي في اللعب. سأرى ما إذا كان بإمكاني التوصل إلى مثال (تحديث القيام به). ملاحظة، ومع ذلك، فإن EF لا يدعم هذا - كنت بحاجة إلى إعادة بناء التعبير من الصفر. لدي بعض التعليمات البرمجية للقيام بذلك، لكنها طويلة جدا ...

رمز التعبير (باستخدام Invoke) بسيط جدا:

var param = Expression.Parameter(typeof(TEntity), "item");
var item = Expression.Invoke(selectItem, param);
var key = Expression.Invoke(selectKey, item);
var lambda = Expression.Lambda<Func<TEntity, TKey>>(key, param);
return source.OrderBy(lambda);

هنا استخدام المثال على Northwind:

using(var ctx = new MyDataContext()) {
    ctx.Log = Console.Out;
    var rows = ctx.Orders.OrderBy(order => order.Customer,
        customer => customer.CompanyName).Take(20).ToArray();
}

مع Tsql (إعادة تهيئة لتناسب):

SELECT TOP (20) [t0].[OrderID], -- snip
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN [dbo].[Customers] AS [t1]
  ON [t1].[CustomerID] = [t0].[CustomerID]
ORDER BY [t1].[CompanyName]

نصائح أخرى

ما لديك هناك sotring، تليها الإسقاط ثم الفرز مرة أخرى.

.OrderBy(x => x.Sub)
    .Select(x => x.Sub)
        .OrderBy(x => x.Key)

قد تكون طريقةك مثل هذا:

public static IOrderedQueryable<TSubEntity> OrderBy<TEntity, TSubEntity, TKey>(
    this IQueryable<TEntity> source, 
    Expression<Func<TEntity, TSubEntity>> selectItem, 
    Expression<Func<TSubEntity, TKey>> selectKey)
    where TEntity : class
    where TSubEntity : class 
{
    return (IOrderedQueryable<TSubEntity>)source.
        OrderBy(selectItem).Select(selectItem).OrderBy(selectKey)
}

سيتم تنفيذ ذلك بواسطة SQL ولكن ربما لاحظت أنه اضطررت إلى تغيير نوع الإرجاع هنا إلى iorderedqueryableu003CTSubEntity> وبعد هل تستطيع العمل في ذلك؟

كنت بحاجة إلى نفس طريقة التمديد هذه:

    /// <summary>
    /// From A.B.C and D.E.F makes A.B.C.D.E.F. D must be a member of C.
    /// </summary>
    /// <param name="memberExpression1"></param>
    /// <param name="memberExpression2"></param>
    /// <returns></returns>
    public static MemberExpression JoinExpression(this Expression memberExpression1, MemberExpression memberExpression2)
    {
        var stack = new Stack<MemberInfo>();
        Expression current = memberExpression2;
        while (current.NodeType != ExpressionType.Parameter)
        {
            var memberAccess = current as MemberExpression;
            if (memberAccess != null)
            {
                current = memberAccess.Expression;
                stack.Push(memberAccess.Member);
            }
            else
            {
                throw new NotSupportedException();
            }
        }


        Expression jointMemberExpression = memberExpression1;
        foreach (var memberInfo in stack)
        {
            jointMemberExpression = Expression.MakeMemberAccess(jointMemberExpression, memberInfo);
        }

        return (MemberExpression) jointMemberExpression;
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top