Consider this class:
public class Column<T>
{
public string Header { get; set; }
public Func<T, string> ValueExpression { get; set; }
}
used like this:
var columns = new List<Column<Employee>>
{
new Column<Employee> {Header = "Employee Id", ValueExpression = e => e.EmployeeID.ToString()},
new Column<Employee> {Header = "Name", ValueExpression = e => e.FirstName + " " + e.LastName},
new Column<Employee> {Header = "Employee Birthday Year", ValueExpression = e => e.BirthDate.HasValue ? e.BirthDate.Value.Year.ToString() : ""},
new Column<Employee> { Header = "test", ValueExpression = e => e.Address}
}
I would like to do a .Select() on an IQueryable to make it only retrieve the needed fields from the database.
So I want to do something like this:
var expressions = columns.Select(c => c.ValueExpression).Combine();
IQueryable<Employee> employees = EmployeeRepository.GetEmployees();
employees = employees.Select(expressions);
Only "Combine()" obviously doesn't exist.. :-)
public static Func<T, U[]> Combine<T, U>(this Func<T, U>[] functions) {
return t => functions.Select(fun => fun(t)).ToArray();
}
I'd declare that for generic IEnumerable<Func<T, U>> instead of array:
public static Func<T, IEnumerable<U>> Combine<T, U>(this IEnumerable<Func<T, U>> functions)
{
return t => functions.Select(fun => fun(t));
}
As mentioned in comments, this is not likely to work directly with LINQ to SQL. However, you could grab LINQ to SQL results by doing a .AsEnumerable() and process the rest on client side.
Related
I really don't like to hard code the name of properties of my models. So I came up with this code so far. My code is working fine and it does exactly what I want but in an ugly way. I'm pretty sure it will be problematic soon. So any help to improve it and make it work in the right way is appreciated. I'm looking to fine better way to extract selected property names without converting expression body to string. Any change to any part of this class is fine with me. Even changing usage as long as I don't hard code my property names.
What is the better way to extract selected properties name of a model?
Here is my code:
public class Selector<T> : IDisposable
{
Dictionary<string, Func<T, object>> Selectors = new Dictionary<string, Func<T, object>>();
public Selector(params Expression<Func<T, object>>[] Selector)
{
foreach (var select in Selector)
{
//string MemberName = CleanNamesUp(select.Body.ToString());
//Func<T, object> NewSelector = select.Compile();
#region Ugly Part 1
Selectors.Add(CleanNamesUp(select.Body.ToString()), select.Compile());
#endregion
}
}
#region I am Doing This So I can Use Using(var sl = new Selector<T>())
public void Dispose()
{
Selectors.Clear();
Selectors = null;
}
#endregion
#region Ugly Part 2
private string CleanNamesUp(string nameStr)
{
string name = nameStr.Split('.')[1];
if (name.Contains(","))
{
name = name.Split(',')[0];
}
return name;
}
#endregion
public Dictionary<string, object> GetFields(T Item)
{
Dictionary<string,object> SetFieldList = new Dictionary<string, object>();
foreach(var select in Selectors)
{
SetFieldList.Add( select.Key , select.Value(Item));
}
return SetFieldList;
}
public List<Dictionary<string, object>> GetFields(IEnumerable<T> Items)
{
List<Dictionary<string, object>> SetFieldListMain = new List<Dictionary<string, object>>();
foreach (var item in Items)
{
Dictionary<string, object> SetFieldList = new Dictionary<string, object>();
foreach (var select in Selectors)
{
SetFieldList.Add(select.Key, select.Value(item));
}
SetFieldListMain.Add( SetFieldList);
}
return SetFieldListMain;
}
internal List<string> GetKeys()
{
return new List<string>(this.Selectors.Keys);
}
}
This is my model:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public bool IsEnabled { get; set; }
public bool IsLocked { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime LockedAt { get; set; }
}
And I am using it like this:
User user1 = new User();
user1.Email = "testDev#gmail.com";
user1.UserName = "dora";
user1.Password = "123456";
var UpObject = new Selector<User>( x => x.UserName, x => x.Password, x => x.Email, x => x.IsEnabled );
Dictionary<string,object> result = UpObject.GetFields(user1);
You can avoid parsing the expressions as string if you instead parse them as System.Linq.Expressions.
Full code sample follows, but not exactly for your code, I used DateTime instead of the generic T, adapting should just be find&replace:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace ExprTest
{
class Program
{
static void Main(string[] args)
{
#region Usage
Expression<Func<DateTime, object>> propertySelector = x => x.Day;
Expression<Func<DateTime, object>> methodSelector = x => x.AddDays(1.5);
Expression[] inputSelectors = new Expression[] { propertySelector, methodSelector };
#endregion
//These are your final Selectors
Dictionary<string, Func<DateTime, object>> outputSelectors = new Dictionary<string, Func<DateTime, object>>();
//This would be in your Selector<T> constructor, replace DateTime with T.
//Instead of CleanNamesUp you would decide which part to use by extracting the appropriate Expression argument's Name.
foreach (Expression<Func<DateTime, object>> selectorLambda in inputSelectors)
{
Expression selectorExpression = selectorLambda.Body;
string name = null;
while (string.IsNullOrEmpty(name))
{
switch (selectorExpression)
{
#region Refine expression
//Necessary for value typed arguments, which get boxed by Convert(theStruct, object)
case UnaryExpression unary:
selectorExpression = unary.Operand;
break;
//add other required expression extractions
#endregion
#region Select expression key/name
case MemberExpression fieldOrProperty:
name = fieldOrProperty.Member.Name;
break;
case MethodCallExpression methodCall:
name = methodCall.Method.Name;
break;
//add other supported expressions
#endregion
}
}
outputSelectors.Add(name, selectorLambda.Compile());
}
//Set a breakpoint here to explore the outputSelectors
}
}
}
There could be a library for this, but i don't know about any, except PredicateBuilder for when you need to unify lambda arguments into one lambda expression.
I think maybe you forgot an important keyword 'nameof'. With the keyword, the code will be like this:
class User
{
public string Name { get; set; }
public string Address { get; set; }
public string Tel { get; set; }
}
static Dictionary<string, object> GetFieldsOf<T>(T item, params string[] args)
{
var properties = args.Select(property => typeof(T).GetProperty(property));
return properties.ToDictionary(property => property.Name, property => property.GetValue(item));
}
static void Main(string[] args)
{
var user = new User { Name = "Abel", Address = "Unknown", Tel = "XXX-XXX" };
var result = GetFieldsOf(user, nameof(User.Name), nameof(User.Address));
}
This code will result in some performance problems caused by reflection. But fortunately, you can avoid these by emitting a small segement of code.
//MSIL
ldarg.0
call Property.GetMethod
ret
And replace it with proerpty.GetValue. These code can be generated and cached per type, which is still worthwhile.
I want to create generic expression filtering in IQueryable
public class Vehicle
{
public int Id { get; set; }
public string VehicleNO { get; set; }
public int DriverId { get; set; }
public Driver Driver {get;set;}
}
public class Driver
{
public int Id { get; set; }
public string Name { get; set; }
}
operator = "Contain", field name = "Driver.Name", Value filter =
"Micheal"
I don't know how to filter driver name.
Here is my full code
IQueryable<SysClientSiteUser> query = entity.SysClientSiteUsers.Include(i => i.SysClientSiteRole);
Dictionary<string, string> dtFilter = new Dictionary<string, string>();
dtFilter.Add("VehicleNo", "A123");
Dictionary<Type, Func<string, object>> lookup = new Dictionary<Type, Func<string, object>>();
lookup.Add(typeof(string), x => { return x; });
lookup.Add(typeof(long), x => { return long.Parse(x); });
lookup.Add(typeof(int), x => { return int.Parse(x); });
lookup.Add(typeof(double), x => { return double.Parse(x); });
var paramExpr = Expression.Parameter(typeof(Vehicle), "VehicleNo");
var keyPropExpr = Expression.Property(paramExpr, "VehicleNo");
if (!lookup.ContainsKey(keyPropExpr.Type))
throw new Exception("Unknown type : " + keyPropExpr.Type.ToString());
var typeDelegate = lookup[keyPropExpr.Type];
var constantExp = typeDelegate("A123");
var eqExpr = Expression.Equal(keyPropExpr, Expression.Constant(constantExp));
var condExpr = Expression.Lambda<Func<SysClientSiteUser, bool>>(eqExpr, paramExpr);
query = query.Where(condExpr);
for normal field, it's working. But if I want to call Driver name. it's not work. How to call "Driver.Name"?
You can use a helper function to convert a nested property name string to an Expression that accesses that property for a given ParameterExpression and type:
private static Expression MakePropertyExpression<T>(string propertyName, Expression baseExpr) =>
propertyName.Split('.').Aggregate(baseExpr, (b, pname) => Expression.Property(b, pname));
I have two expressions like this:
Expression<Func<T, T> exp1 = x => new T { Id = 1 , Name = "string"}
Expression<Func<T, T> exp2 = x => new T { Age = 21 }
How can I merge them?
Result:
Expression<Func<T, T> exp3 = x => new T { Id = 1 , Name = "string" , Age = 21}
T is : IEntityBaseRepository<T> where T : class, IEntityBase, new()
IEntityBase:
public interface IEntityBase
{
string Id { get; set; }
string Name { get; set; }
int Age { get; set; }
}
public virtual async Task UpdateAsync(string id, Expression<Func<T, T>> updateFactory)
{
Expression<Func<T, T>> updateFactory2 = s => new T { age = 21 };
//Expression<Func<T, T>> updateFactoryFinal = updateFactory + updateFactory2;
await _context.Set<T>().Where(w => w.Id == id).UpdateAsync(updateFactoryFinal);
}
Because you never use Func argument you can simplify your code and use Func<T>. Next method will merge two (or more) MemberInitExpression into one:
public static Expression<Func<T>> MergeExpressions<T>(params Expression<Func<T>>[] expressions)
where T : new()
{
var allBindings = new List<MemberBinding>();
foreach (var expression in expressions)
{
var bindings = ((MemberInitExpression) expression.Body).Bindings;
allBindings.AddRange(bindings);
}
var body = Expression.MemberInit(Expression.New(typeof(T)), allBindings);
return Expression.Lambda<Func<T>>(body);
}
You can check it online here
Is it possible to build part of query based on Expression?
public List<SelectListItem> GetSelectedListFromEntity<T>(Expression<Func<T, object>> property) where T : BaseEntity<int>
{
var result = _repository.Query<T>().Select(p => new SelectListItem()
{
Text = property, //? (in simple case it looks like: p.Name + p.Category)
Value = p.Id.ToString(),
).ToList();
return result;
}
For:
var result = GetSelectedListFromEntity<Product>(p => p.Name + p.Category);
If you want it to work with a db provider, something giving you an IQueryable you can do something with expression trees like this.
Given these classes:
public class BaseEntity
{
public int Id { get; set;}
}
public class Product : BaseEntity
{
public string Name { get; set; }
public string Category { get; set; }
}
Now you need a function to make the expression:
public Expression<Func<T, SelectListItem>> CreateExpression<T>(Expression<Func<T, string>> textExp) where T : BaseEntity
{
var arg = textExp.Parameters.First();
var param = new ParameterExpression[] { arg };
var body = Expression.MemberInit(
Expression.New(typeof(SelectListItem)),
Expression.Bind(typeof(SelectListItem).GetMember("Text").First(), textExp.Body),
Expression.Bind(typeof(SelectListItem).GetMember("Value").First(),
Expression.Call(
Expression.PropertyOrField(arg, "Id"),
"ToString",
new Type[0]
)));
var exp = Expression.Lambda(body, param);
return (Expression<Func<T, SelectListItem>>)exp;
}
In C# 6 it's better practice to replace those magic strings with nameof().
Then finally you can call it with something like this:
var items = new List<Product> { new Product { Id = 0, Name = "Test", Category = "Cat" } };
var result = items.AsQueryable().Select(CreateExpression<Product>(p => p.Name + p.Category)).ToList();
Now as long as you linq provider can cope with making SelectListItems you should be fine.
I have a page that allow the user to search by multiple fields. One can be used or all. The user can set the operator per field to Equals, Contains, Starts With, etc... Looks like this
I am using EntityFrame Work and retrieving the data using a lamba like this one:
listOfPeople = adDB.People.Where(x => x.LastName.StartsWith(lastName) && x.FirstName.StartsWith(firstName)).OrderBy(x => x.LastName)
The question, How do I dynamically create the where clause depending on the data provided by the user?
You can use something along the lines of a Func factory to do this since the where clause takes in a Func.
Example:
public class Program
{
public static void Main(string[] args)
{
var people = new[]
{
new Person {FirstName = "Hello", LastName = "World"},
new Person {FirstName = "Foo", LastName = "Bar"},
};
Console.WriteLine(people.Where(FuncFactory.GetFilterFunc<Person>(FilterType.Contains, x => x.FirstName, "ello")).Any());
Console.WriteLine(people.Where(FuncFactory.GetFilterFunc<Person>(FilterType.Equals, x => x.FirstName, "ello")).Any());
Console.WriteLine(people.Where(FuncFactory.GetFilterFunc<Person>(FilterType.Contains, x => x.LastName, "ar")).Any());
Console.WriteLine(people.Where(FuncFactory.GetFilterFunc<Person>(FilterType.Equals, x => x.LastName, "ar")).Any());
Console.ReadKey();
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public enum FilterType
{
Contains,
Equals
}
public static class FuncFactory
{
public static Func<T, bool> GetFilterFunc<T>(FilterType filterType, Func<T, IComparable> propFunc, string filter)
{
switch (filterType)
{
case FilterType.Contains:
return x => (propFunc(x) as string).Contains(filter);
case FilterType.Equals:
return x => (propFunc(x) as string).Equals(filter);
default:
throw new ArgumentException("Invalid FilterType");
}
}
}
You probably want to use the Predicate Builder:
http://www.albahari.com/nutshell/predicatebuilder.aspx