2

I have several methods with a common pattern, i want to write a generic function that will take the column name as input and give the same result.

private void Query138()
{
    var products = (from p in _context.Products
                    where p.Manufacturer == null
                    select p);
    foreach (var productItem in products)
    {
        productItem.Manufacturer = string.Empty;
        _context.UpdateProduct(productItem);
    }

}

private void Query139()
{
    var products = (from p in _context.Products
                    where p.ModelNumber == null
                    select p);
    foreach (var productItem in products)
    {
        productItem.ModelNumber = string.Empty;
        _context.UpdateProduct(productItem);
    }

}

i want to write a generic function that will take the column name as input for the above methods.

Example:

 private void Update(Expression<Fun<T,string>> pred = null)
        {
            //use the input column to select the data

            //use the input column to set the new value and update

        }
sudhansu63
  • 6,025
  • 4
  • 39
  • 52

3 Answers3

1

You will need two expressions to achive this. One to get your value and the other to set it.

private void Update(Expression<Func<T, string>> getter, Expression<Action<T, string>> setter)
    {
        var products = (from p in _context.Products
                        where getter(p) == null
                        select p);
        foreach (var productItem in products)
        {
            setter(productItem, string.Empty);
            _context.UpdateProduct(productItem);
        }
    }

This way you can use i => i.[Your Attribute] and (i, o) => i.[Your Attribute] = o to call your function:

Update(i => i.[Your Attribute],(i, o) => i.[Your Attribute] = o); 

Or in your case:

Update(i => i.Manufacturer,(i, o) => i.Manufacturer = o); 
Update(i => i.ModelNumber,(i, o) => i.ModelNumber = o); 
Hyarus
  • 922
  • 6
  • 14
1

This should works:

class SomeClass
{
     public string prop1 { get; set; }
     public string prop2 { get; set; }
}

///...

private void Update<T, TReturn>(Expression<Func<T, TReturn>> expression) 
{
    MemberExpression body = (MemberExpression)expression.Body;
    var propName = body.Member.Name;
    var prop = typeof(SomeClass).GetProperty(propName);
    var products = (from p in contextProds
                    where prop.GetValue(p) == null
                    select p);
    ///...
}

Usage:

Update((SomeClass s) => s.prop1);
Update((SomeClass s) => s.prop2);

Where contextProds is like List<SomeClass> type. Also you can visit this: Get name of property as a string.

Zhuravlev A.
  • 386
  • 4
  • 12
  • this will probably work for LINQ to Entities.I am getting exception in LINQ to SQL: Method 'System.Object GetValue(System.Object)' has no supported translation to SQL. – sudhansu63 Apr 11 '18 at 04:07
  • Had you been watching this, @sudhAnsu63? https://stackoverflow.com/questions/22104050/linq-to-entities-does-not-recognize-the-method-system-object-getvalue – Zhuravlev A. Apr 11 '18 at 14:42
0

You can specify a getter and use it to generate a setter. This can be achieved as follows:

private void Update(Expression<Func<Product, string>> getExpr)
{
    var value = Expression.Parameter(typeof(string), "value");
    var setExpr = Expression.Lambda<Action<Product, string>>(
        Expression.Assign(getExpr.Body, value), getExpr.Parameters[0], value);

    Func<Product, string> getter = getExpr.Compile();
    Action<Product, string> setter = setExpr.Compile();

    var products = (from p in _context.Products
                    where getter(p) == null
                    select p);
    foreach (var productItem in products)
    {
        setter(productItem, string.Empty);
        _context.UpdateProduct(productItem);
    }
}

And then called as follows:

Update( x => x.ModelNumber );
Peter Aylett
  • 750
  • 3
  • 8
  • 1
    Instead of getter `Func` you'd better build `Expression` and use it directly in `_context.Products.Where`. The way you are suggesting currently will cause retrieving the whole table and performing the filtering client side. – Ivan Stoev Apr 06 '18 at 13:11
  • LINQ to SQL exception: Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL. Probably work on LINQ to Entities. – sudhansu63 Apr 11 '18 at 04:11