question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Please add a method With to be able to change values of a few object properties only. Imagine you have a table with 30 columns C1, C2, … C30.

var q = from t in db.Tbl
             select t.With(new Tbl { C13 = t.C7});

This should work the same as

var q = from t in db.Tbl
        select new Tbl
      {
        C1 = t.C1,
        C2 = t.C2,
        ...
        C12 = t.C12,
        C13 = t.C7, // <-- !
        C14 = t.C14,
        ...
        C30 = t.C30
      };

So you don’t need to copy the long list of properties just to change a single value.

With expressions introduced in C# 9 seems aren’t allowed in expression trees so we need an extension method With.

============================

[Extension("", ServerSideOnly = true, BuilderType = typeof(WithBuilder))]
public static T With<T>(this T @this, T newInit) =>
	throw new LinqException($"'{nameof(With)}' is only server-side method.");

class WithBuilder : IExtensionCallBuilder
{
	public void Build(ISqExtensionBuilder builder)
	{
		Expression? @this = builder.Arguments[0];
		MemberInitExpression? newInit = builder.Arguments[1] as MemberInitExpression ?? throw new ArgumentException();
		// ...
		builder.ResultExpression = ???;
	}
}

The type of builder.ResultExpression is ISqlExpression. I can create another MemberInitExpression that will copy all properties from @this except the ones used in newInit. But I couldn’t create a correct value of builder.ResultExpression.

It would be nice if Linq2Db provided a possibility to replace expression tree(s) with another expression tree (some IExtensionCallBuilder2 which ResultExpression type is also a System.Linq.Expressions.Expression) so you wouldn’t need to manually create the lower-level ISqlExpressions. And that should work on some earlier stage of the query processing.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
sdanylivcommented, Aug 20, 2021

You can Implement IExpressionPreprocessor in your DataConnection class.

0reactions
artelkcommented, Aug 19, 2021

The following (incomplete) solution works:

Code
public static IQueryable<T> Process<T>(this IQueryable<T> q)
{
    Patch((IExpressionQuery<T>)q);
    return q;
}

private static void Patch<T>(IExpressionQuery<T> q)
{
    q.Expression = MyExpressionVisitor.Instance.Visit(q.Expression); // hack
}

[AttributeUsage(AttributeTargets.Method)]
public class RawExpressionMethodAttribute: Attribute
{
    public RawExpressionMethodAttribute(string methodName)
    {
        MethodName = methodName;
    }

    public string MethodName { get; }
}

class MyExpressionVisitor : ExpressionVisitor
{
    public static readonly MyExpressionVisitor Instance = new MyExpressionVisitor();

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.IsStatic && node.Method.GetCustomAttribute<RawExpressionMethodAttribute>() is { } attr)
        {
            var method = node.Method.DeclaringType.GetMethod(attr.MethodName,
                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static,
                    null,
                    node.Method.GetParameters().Select(p => typeof(Expression)).ToArray(),
                    null);
            if (method == null || !method.ReturnType.IsAssignableTo(typeof(Expression)))
            {
                throw new LinqException();
            }

            var expr = (Expression)method.Invoke(null, node.Arguments.ToArray());
            return base.Visit(expr);
        }

        return base.VisitMethodCall(node);
    }
}
Usage
[RawExpressionMethod(nameof(WithImpl))]
public static T With<T>(this T @this, T newInit) =>
    throw new LinqException($"'{nameof(With)}' can only be used in query.");

private static Expression WithImpl(Expression @this, Expression newInit)
{
    var binds = @this.Type.GetProperties()
        .Where(p => p.GetCustomAttribute<AssociationAttribute>() is null)
        .ToDictionary<PropertyInfo, string, MemberBinding>(prop => prop.Name,
                p => Expression.Bind(p, Expression.Property(@this, p)));
    foreach (var bind in ((MemberInitExpression)newInit).Bindings)
    {
        binds[bind.Member.Name] = bind;
    }
    var newExpr = Expression.MemberInit(Expression.New(@this.Type.GetConstructor(Array.Empty<Type>())), binds.Values);
    return newExpr;
}

//...

var q = from t1 in db.Tbl1
        select t1;
q = q.Select(t => t.With(new Tbl1 { C13 = t.C7 }));
q = q.Process(); // <--- required
var list = q.ToList();

The Process method replaces all occurrences of the methods with RawExpressionMethod attribute with the results of the Impl methods. It should be called before the engine does some caching etc. I think it would be nice if Linq2Db provided something like RawExpressionMethodAttribute and executed the replacing Visitor by itself.

Read more comments on GitHub >

github_iconTop Results From Across the Web

With Definition & Meaning
1. a. : in opposition to : against 2. : in shared relation to. talking with a friend 3. : in regard to...
Read more >
With Definition & Meaning
a combining form of with, having a separative or opposing force: withstand; withdraw. Origin of with-. 2. Middle English, Old English ...
Read more >
WITH | definition in the Cambridge English Dictionary
used of people or things that are together or doing something together: She's in the kitchen with Dad. He's an impossible person to...
Read more >
With Definition & Meaning | Britannica Dictionary
WITH meaning: 1 : used to say that people or things are together in one place; 2 : used to say that two...
Read more >
With definition in American English
With definition: If one person is with another, they are together in one place. | Meaning, pronunciation, translations and examples in American English....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found