Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
298 views
in Technique[技术] by (71.8m points)

c# - Entity Framework: Modify only one field in Select projection

I have this EF query:

var customers = customers.SelectMany(ps => ps.ProductSales.Select(c => new Customer() {
return customers.Select(i => new Customer {
    FullName = i.FullName,
    Birthday = i.Birthday, 
    Score = i.Score,
    // Here, I've got more fields to fill
    ProductSales= new List<ProductSales>() { p }
}).ToList();

In other words, I only want one or two fields of the list of the customers to be modified based on a condition, in my business method. I've got two ways to do this,

  1. Using for...each loop, to loop over customers and modify that field (imperative approach)
  2. Using LINQ projection (declarative approach)

Is there any technique to be used in LINQ query, to only modify one property in projection? For example, something like:

return customers.SelectMany(ps => ps.ProductSales.Select(c => new Customer()
        .Select(i => new Customer {
           result = i // telling LINQ to fill other properties as it is
           ProductSales= new List<ProductSales>() { p } // then modifying this one property
}).ToList();

Actual class contains 30 members, don't want to rewrite everything

*Please keep result in IQueryable format

Trying to keep as One Select, so I can grab nested properties if possible.

Currently using EF Core 3.1

Resources: based off this question using regular linq in 2010 (this syntax does not work in EF Core 3.1)

How to modify only one or two field(s) in LINQ projections?

question from:https://stackoverflow.com/questions/66057069/entity-framework-modify-only-one-field-in-select-projection

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

So, if I understand the question, you have a projection expression of some kind;

Expression<Func<T,T>> projection = t => new T{ Prop = ... };
// eg; query = query.Select(projection);

And you want to ensure that any missing properties of typeof(T) are added to the expression tree.

I would implement an ExpressionVisitor to locate the MemberInitExpression and add any missing Bindings.

    public class MyVisitor : ExpressionVisitor
    {
        private ParameterExpression parm;

        protected override Expression VisitMemberInit(MemberInitExpression node)
        {
            var existing = node.Bindings.Select(b => b.Member).ToHashSet();

            var missing = node.Type.GetProperties()
                .Except(existing)
                .ToList();

            if (missing.Count > 0)
            {
                return node.Update(
                    node.NewExpression,
                    node.Bindings.Concat(
                        missing.Select(p =>
                            Expression.Bind(
                                p,
                                Expression.MakeMemberAccess(parm, p)
                            ))));
            }
            return node;
        }
        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            parm = node.Parameters.Single();
            return base.VisitLambda(node);
        }
    }

    public class X
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

    public class Example{
        [Fact]
        public void Test()
        {
            Expression<Func<X, X>> expr = x => new X { Name = "Test" };
            expr = new MyVisitor().VisitAndConvert(expr, "");
            // expr should now be equivalent to `x => new X { Name = "Test", Id = x.Id };`

            // then;
            // query = query.Select(expr);
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...