I work with Dapper and I try to create auto-mapped method for inner join.
This is the example of the models:
public class User
{
public long IdUser { get; set; }
public string Email { get; set; }
}
public class Page
{
public long Id { get; set; }
public string Name { get; set; }
public long IdUserCreatedPage { get; set; }
public User UserCreatedPage { get; set; }
}
This is the query:
SELECT * FROM "PAGE" INNER JOIN "USER" ON "PAGE"."IdUserCreatedPage" = "USER"."IdUser"
if I write code mannualy I will write this:
public List<Page> GetPage(IDbConnection dbConnection, string sql)
{
return (List<Page>)dbConnection.Query<Page, User, Page>(sql,
(Page p, User u) =>
{
p.UserCreatedPage = u;
return p;
},
splitOn: "IdUser").ToList();
}
Now, what I want is create dynamically the Func<TFirst, TSecond, TOut> that I need for mapping the object.
Can someone help me? Thank you very much.
P.S. I know that in this case it does not make sense create it dynamically, but this it's only a simply version of all the project of auto-mapping.
SOLUTION
Finally I find the way to do what I want.
This is the code of the function that Generate Func<TFirst, TSecond, TOut>:
public static Func<TFirst, TSecond, TFirst> MappingDynamicFunc<TFirst, TSecond>()
{
ParameterExpression paramFirst = Expression.Parameter(typeof(TFirst), "paramFirst");
ParameterExpression paramSecond = Expression.Parameter(typeof(TSecond), "paramSecond");
MemberExpression memberExpression = Expression.PropertyOrField(paramFirst, "UserCreatedPage");
BinaryExpression assign = Expression.Assign(memberExpression, paramSecond);
LabelTarget labelTarget = Expression.Label(typeof(TFirst));
GotoExpression returnExpression = Expression.Return(labelTarget, paramFirst, typeof(TFirst));
LabelExpression labelExpression = Expression.Label(labelTarget, Expression.Default(typeof(TFirst)));
BlockExpression block = Expression.Block(
assign,
returnExpression,
labelExpression
);
return Expression.Lambda<Func<TFirst, TSecond, TFirst>>(block, new ParameterExpression[] { paramFirst, paramSecond }).Compile();
}
And this is the "GetPage" method:
public List<Page> GetPage(IDbConnection dbConnection, string sql)
{
return (List<Page>)dbConnection.Query<Page, User, Page>(sql,
MappingDynamicFunc<Page, User>(),
splitOn: "IdUser").ToList();
}
Take a look at PredicateBuilder. http://www.albahari.com/nutshell/predicatebuilder.aspx
Here is some pseudo code
var predicate = PredicateBuilder.True<SomeClass>();
if (SomeCondition)
{
var inner = PredicateBuilder.False<SomeClass>();
inner = inner.Or(p => p.Category == "WhatEver");
inner = inner.Or(p => p.Category == "");
predicate = predicate.And(inner);
}
...
var result = MyIEnumerable<SomeClass>.AsQueryable()
.Where(predicate)
.FirstOrDefault();
Related
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 a LINQ expression that I wish to make it become generic :
public class GenericBudgetMatrix
{
public List<string> MatrixHeaders { set; get; }
public List<GenericBudgetMatrixRow> MatrixRow { set; get; }
public GenericBudgetMatrixRow MatrixFooterRow { set; get; }
}
public class GenericBudgetMatrixRow
{
public string EntityName { set; get; }
public List<decimal> Values { set; get; }
public decimal Total { set; get; }
}
GenericBudgetMatrix _genericBudgetMatrix = new GenericBudgetMatrix();
_genericBudgetMatrix.MatrixRow = _matrixTemplate.AsEnumerable().Select(r =>
new GenericBudgetMatrixRow
{
EntityName = r.Field<string>(0),
Values = r.ItemArray.Skip(1).Select(x => Convert.ToDecimal(x)).ToList(),
Total = r.ItemArray.Skip(1).Select(x => Convert.ToDecimal(x)).Sum()
}
).ToList();
I wish to make a new method to accept
r =>
new GenericBudgetMatrixRow
{
EntityName = r.Field<string>(0),
Values = r.ItemArray.Skip(1).Select(x =>
Convert.ToDecimal(x)).ToList(),
Total = r.ItemArray.Skip(1).Select(x =>
Convert.ToDecimal(x)).Sum()
}
as a Func expression parameter.
Something like this :
public void GenericMethod(Func<expression> predicate, DataTable _matrixTemplate)
{
_matrixTemplate.AsEnumerable().Select(predicate).ToList();
}
Any ideas that can help me build this approach?
Select expects a Func<T, TResult>, so you should provide it as parameter to your method:
public void GenericMethod<T, TResult>(Func<T, TResult> predicate, DataTable _matrixTemplate)
{
_matrixTemplate.AsEnumerable().Select(predicate).ToList();
}
Furthermore I guess your method should return the result of your selection, so turn it into a method returning instances of TResult(which in your case is GenericBudgetMatrixRow). Last but not least the call to ToList is obsolete as you´re doing this in your calling code anyway. Thus return an IEnumerable<TResult> instead:
public IEnumerable<TResult> GenericMethod<T, TResult>(Func<T, TResult> predicate, DataTable _matrixTemplate)
{
return _matrixTemplate.AsEnumerable().Select(predicate);
}
Now you cann call it like this:
_genericBudgetMatrix.MatrixRow = GenericMethod(
r => new GenericBudgetMatrixRow
{
EntityName = r.Field<string>(0),
Values = r.ItemArray.Skip(1).Select(x => Convert.ToDecimal(x)).ToList(),
Total = r.ItemArray.Skip(1).Select(x => Convert.ToDecimal(x)).Sum()
},
_matrixTemplate)
.ToList();
I figured out the solution already. It is due to the Generic Type
public IEnumerable<TResult> GenericMethod(Func<DataRow, TResult> predicate, DataTable _matrixTemplate)
{
return _matrixTemplate.AsEnumerable().Select(predicate);
}
I am to build a dynamic Expression Tree for GroupBy. All i want to achieve is like this.
var NestedGrouped = listOfPerson.GroupByMany(x => x.Name,x=>x.Age).ToList();
My Person Class is like :-
class Person
{
public string Name{ get; set; }
public int Age{ get; set; }
public float Salary{ get; set; }
}
public class GroupResult
{
public object Key { get; set; }
public int Count { get; set; }
public IEnumerable Items { get; set; }
public IEnumerable<GroupResult> SubGroups { get; set; }
public override string ToString()
{ return string.Format("{0} ({1})", Key, Count); }
}
public static class MyEnumerableExtensions
{
public static IEnumerable<GroupResult> GroupByMany<TElement>(
this IEnumerable<TElement> elements,
params Func<TElement, object>[] groupSelectors)
{
if (groupSelectors.Length > 0)
{
var selector = groupSelectors.First();
//reduce the list recursively until zero
var nextSelectors = groupSelectors.Skip(1).ToArray();
return
elements.GroupBy(selector).Select(
g => new GroupResult
{
Key = g.Key,
Count = g.Count(),
Items = g,
SubGroups = g.GroupByMany(nextSelectors)
});
}
else
return null;
}
}
For Single Property I am able to build the expression but i want to do GROUPBY
with multiple columns as shown above.
FOR SINGLE PROPERTY :-
ParameterExpression parameter = Expression.Parameter(typeof(Person), "lambdaKey");
var menuProperty = Expression.PropertyOrField(parameter, "Name");
var lambda = Expression.Lambda<Func<Person, string>>(menuProperty, parameter);
var selector = lambda.Compile();
var result = P1.GroupByMany(selector);// P1 is list of PERSON
How to ADD multiple columns in Expression Tree (e.g (x => x.Name,x=>x.Age)).
Please Help. Thanks in Advance.
GroupByMany() accepts array of delegates, one delegate for each key. So, what you need is to create and compile a separate expression for each key.
The code could look something like:
private static Func<TElement, object> CreateSelector<TElement>(string key)
{
var parameter = Expression.Parameter(typeof(TElement), "lambdaKey");
var property = Expression.PropertyOrField(parameter, key);
var lambda = Expression.Lambda<Func<TElement, string>>(property, parameter);
return lambda.Compile();
}
public static IEnumerable<GroupResult> GroupByMany<TElement>(
this IEnumerable<TElement> elements,
params string[] groupKeys)
{
return elements.GroupByMany(groupKeys.Select(CreateSelector<TElement>).ToArray());
}
I use generic mode of function with a parameter as TEntity
for example TEntity is Person
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Family { get; set; }
public string MobileNo { get; set; }
public int Age { get; set; }
}
I need generate an Expression Tree like the following (automaticaly):
Expression<Func<TEntity, bool>> :
x=> x.ID = 123 && x.Name="AAA" && x.Family="BBB"
for return type of below method
public Expression<Func<TEntity, bool>> SearchExpression()
{
HERE !!!
}
Can anyone help me for this purpose ?
Based on your description / comments, the following should work for you:
public Expression<Func<TEntity, bool>> SearchExpression()
{
ConstantExpression[] expectedValues = Your_Magic_Method_Of_Obtaining_Expected_Values();
var entity = Expression.Parameter(typeof (TEntity));
var comparisonExpression = typeof(TEntity).GetProperties()
.Select((info, i) => Expression.Equal(
Expression.Property(entity, info),
expectedValues[i]))
.Aggregate(Expression.And);
return Expression.Lambda<Func<TEntity, bool>>(comparisonExpression, entity);
}
public List<Expression<Func<TEntity, bool>>> Filters { get; set; } = new List<Expression<Func<TEntity, bool>>>();
implement IQueryable
if (xxx.Filters != null && xxx.Filters.Any())
{
foreach (var filter in xxx.Filters)
{
list = list.Where(filter);
}
}
set filters
xxx.Filters.Add(x => x.YourProperty1Name.Contains("98"));
xxx.Filters.Add(x => x.YourProperty2Name.Equals("abc"));
In response to this question:
Better way to write this linq query?
How would i build a dynamic query following the same pattern in that thread?
For example, the signature of the method changes to:
public List<PeopleSearchList> GetPeople(string filter, string searchType, string searchOption)
{
return a new List of type PeopleSearchList
}
So now i am not returning a single array of "Firstnames" etc i am returning a custom class.
The class would look like this:
public class PeopleSearchList
{
public String IdentityCode { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
public Int32 LoanCount { get; set; }
public String Group { get; set; }
}
I worked it out.
Just thought i would post the solution for others to see.
public List<PeopleSearchList> GetPeople(string filter, string searchType, string searchOption)
{
IQueryable<Person> query = _context.People;
PropertyInfo property = typeof (Person).GetProperty(searchType);
MethodInfo method = typeof (string).GetMethod(searchOption, new[] {typeof (string)});
query = query.Where(WhereExpression(property, method, filter));
IQueryable<PeopleSearchList> resultQuery = query.Select(p => new PeopleSearchList
{
Firstname = p.Firstname,
Group = p.Level.Year,
IdentityCode = p.IdentityCode,
LoanCount = p.Loans.Count(),
Surname = p.Surname
}
).OrderBy(p => p.Surname);
return resultQuery.ToList();
}
Expression<Func<Person, bool>> WhereExpression(PropertyInfo property, MethodInfo method, string filter)
{
var param = Expression.Parameter(typeof(Person), "o");
var propExpr = Expression.Property(param, property);
var methodExpr = Expression.Call(propExpr, method, Expression.Constant(filter));
return Expression.Lambda<Func<Person, bool>>(methodExpr, param);
}