Reflection in C# Case studies in metaprogramming torsdag 6 mars 14

advertisement
Reflection in C#
Case studies in metaprogramming
torsdag 6 mars 14
GenericDAO
torsdag 6 mars 14
ORM for legacy DB
• DB with T-SQL Stored Procedures (SP)
• Apps written in C#,VB .Net and VB6
• Domain classes require mapping classes
• Different SP:s return different sets of data
for the same domain objects
• Related data..
torsdag 6 mars 14
1 Domain class 1 DAO
Interface Layer
UI
Model
Service Layer
Domain
Model
Data Access Layer
Database
torsdag 6 mars 14
1 Domain class 1 DAO
Interface Layer
UI
Model
Service Layer
Domain
Model
Data Access Layer
Database
torsdag 6 mars 14
1 Domain class 1 DAO
Interface Layer
LoginService
login(string user)
logout(string user)
UI
Model
Service Layer
Domain
Model
Data Access Layer
Person
firstName
lastName
employments
address
PersonDAO
getAll()
get(int id)
getByUserName(string userName)
...
SQL query
tabulated result sets
Database
DB
torsdag 6 mars 14
1 Domain class 1 DAO
Interface Layer
LoginService
login(string user)
logout(string user)
UI
Model
Service Layer
Domain
Model
Data Access Layer
Person
firstName
lastName
employments
address
PersonDAO
getAll()
get(int id)
getByUserName(string userName)
...
SQL query
tabulated result sets
Database
GradeDAO
Grade
grade
date
Course
code
credits
teacher
students
torsdag 6 mars 14
getAll()
get(int id)
getBy(string studentName, string
courseName)
CourseDAO
...
getAll()
get(int id)
getByName(string courseName)
...
DB
Related data
var people = new PersonDAO().GetAll()
Person
firstName: "Ola"
lastName: "Leifler"
employments
address
Person
firstName: "Berit"
lastName: "Larsson"
employments
address
people.SelectMany(p => p.Employments)
Do we know if employments have been fetched?
What if we only want to make one DB call?
(Select 1+N Problem)
torsdag 6 mars 14
null
null
null
null
Legacy support
Person
firstName
lastName
employments
address
PersonDAO
getAll()
get(int id)
getByEmployment(int id)
getWithAddress(int id)
"getAll"
DB
fName: "Ola"
lName: "Leifler"
fName: "Berit"
lName: "Jansson"
PersonDAO
getAll()
get(int id)
getByEmployment(int id)
getWithAddress(int id)
"getWithAddress"
DB
firstName: "Ola"
llastName: "Leifler"
streetaddress: "Storgatan 1"
city: "Linköping"
torsdag 6 mars 14
Legacy support
Person
firstName
lastName
employments
address
PersonDAO
getAll()
get(int id)
getByEmployment(int id)
getWithAddress(int id)
"getAll"
DB
fName: "Ola"
lName: "Leifler"
fName: "Berit"
lName: "Jansson"
PersonDAO
getAll()
get(int id)
getByEmployment(int id)
getWithAddress(int id)
"getWithAddress"
DB
firstName: "Ola"
llastName: "Leifler"
streetaddress: "Storgatan 1"
city: "Linköping"
torsdag 6 mars 14
Legacy support
Person
firstName
lastName
employments
address
PersonDAO
getAll()
get(int id)
getByEmployment(int id)
getWithAddress(int id)
"getAll"
DB
fName: "Ola"
lName: "Leifler"
Address
streetaddress
city
fName: "Berit"
lName: "Jansson"
PersonDAO
getAll()
get(int id)
getByEmployment(int id)
getWithAddress(int id)
"getWithAddress"
DB
firstName: "Ola"
llastName: "Leifler"
streetaddress: "Storgatan 1"
city: "Linköping"
torsdag 6 mars 14
Solution
• 1 Generic Data Access Object class
• Dynamic name resolution of Stored
Procedures
• Generated proxy classes for lazy loading
• Markup and configuration to configure
loading related objects, and mapping
between data fields and class properties
torsdag 6 mars 14
1 Generic DAO
var people = GenericDAO<Domain.Person>.Get("GetPeopleByRole", new { RoleId = 3 });
people.Should().NotBeEmpty();
torsdag 6 mars 14
1 Generic DAO
var people = GenericDAO<Domain.Person>.Get("GetPeopleByRole", new { RoleId = 3 });
people.Should().NotBeEmpty();
private static ICollection<T> GetEntities(string procedureName,object parameterObject,
int limit) {
int recordsRead=0;
var config=GetConfig(procedureName);
IList<T> entities=new ConcurrentList<T>();
try {
using(DbCommand command=BuildCommand(procedureName,parameterObject)) {
using(IDataReader reader=command.ExecuteReader()) {
var fieldNames=reader.Names();
config.Init(command,fieldNames);
var allFieldsUsed=config.GetAllFieldsUsedBy(reader);
if((config.Policy&
GenericDAO.ExceptionPolicy.AbortOnFieldsUnused)!=0&&
!fieldNames.IsSubSetOf(allFieldsUsed)) {
// Abort if some SQL result fields are unused
throw new FieldsUnusedFromQueryException(fieldNames.Except(allFieldsUsed));
}
// Add a mapping from the SP to the properties it can set on the current object
while(reader.Read()&&++recordsRead<limit) {
entities.Add(config.InjectFrom(reader));
}
}
}
entities=config.Process(entities);
} finally {
DBAccess.CloseConnection();
}
return entities;
}
torsdag 6 mars 14
1 Generic DAO
var people = GenericDAO<Domain.Person>.Get("GetPeopleByRole", new { RoleId = 3 });
people.Should().NotBeEmpty();
private static ICollection<T> GetEntities(string procedureName,object parameterObject,
int limit) {
int recordsRead=0;
var config=GetConfig(procedureName);
IList<T> entities=new ConcurrentList<T>();
try {
using(DbCommand command=BuildCommand(procedureName,parameterObject)) {
using(IDataReader reader=command.ExecuteReader()) {
var fieldNames=reader.Names();
config.Init(command,fieldNames);
var allFieldsUsed=config.GetAllFieldsUsedBy(reader);
if((config.Policy&
GenericDAO.ExceptionPolicy.AbortOnFieldsUnused)!=0&&
!fieldNames.IsSubSetOf(allFieldsUsed)) {
// Abort if some SQL result fields are unused
throw new FieldsUnusedFromQueryException(fieldNames.Except(allFieldsUsed));
}
// Add a mapping from the SP to the properties it can set on the current object
while(reader.Read()&&++recordsRead<limit) {
entities.Add(config.InjectFrom(reader));
}
}
}
entities=config.Process(entities);
} finally {
DBAccess.CloseConnection();
}
return entities;
}
torsdag 6 mars 14
1 Generic DAO
var people = GenericDAO<Domain.Person>.Get("GetPeopleByRole", new { RoleId = 3 });
people.Should().NotBeEmpty();
private static ICollection<T> GetEntities(string procedureName,object parameterObject,
int limit) {
int recordsRead=0;
var config=GetConfig(procedureName);
IList<T> entities=new ConcurrentList<T>();
try {
using(DbCommand command=BuildCommand(procedureName,parameterObject)) {
using(IDataReader reader=command.ExecuteReader()) {
var fieldNames=reader.Names();
config.Init(command,fieldNames);
var allFieldsUsed=config.GetAllFieldsUsedBy(reader);
if((config.Policy&
GenericDAO.ExceptionPolicy.AbortOnFieldsUnused)!=0&&
!fieldNames.IsSubSetOf(allFieldsUsed)) {
// Abort if some SQL result fields are unused
throw new FieldsUnusedFromQueryException(fieldNames.Except(allFieldsUsed));
}
// Add a mapping from the SP to the properties it can set on the current object
while(reader.Read()&&++recordsRead<limit) {
entities.Add(config.InjectFrom(reader));
}
}
}
entities=config.Process(entities);
} finally {
DBAccess.CloseConnection();
}
return entities;
}
torsdag 6 mars 14
Dynamic name
resolution
torsdag 6 mars 14
Dynamic name
resolution
GetAll()
GetPeopleByRole()
x 500
torsdag 6 mars 14
Dynamic name
resolution
GetAll()
GetPeopleByRole()
x 500
torsdag 6 mars 14
”GetAll”
”GetPeopleByRole”
x 500
Dynamic name
resolution
GetAll()
GetPeopleByRole()
x 500
torsdag 6 mars 14
”GetAll”
”GetPeopleByRole”
x 500
Dynamic name
resolution
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Dynamic;
Configuration;
Extensions;
GetAll()
GetPeopleByRole()
x 500
”GetAll”
”GetPeopleByRole”
x 500
namespace Core {
public class Dispatcher<T>:DynamicObject where T:class, new() {
[ ... ]
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args,
out object result) {
string procedureName=binder.Name;
object paramObj=GetParameterObject(binder,args);
string storedProcedureName=GenericDAO<T>.StoredProcedureFullName(procedureName);
if(procedureName.StartsWith("Get")) {
result=GetResult(paramObj,storedProcedureName);
} [ ... ]
}
}
torsdag 6 mars 14
Dynamic name
resolution
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Dynamic;
Configuration;
Extensions;
GetAll()
GetPeopleByRole()
x 500
”GetAll”
”GetPeopleByRole”
x 500
namespace Core {
public class Dispatcher<T>:DynamicObject where T:class, new() {
[ ... ]
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args,
out object result) {
string procedureName=binder.Name;
object paramObj=GetParameterObject(binder,args);
string storedProcedureName=GenericDAO<T>.StoredProcedureFullName(procedureName);
if(procedureName.StartsWith("Get")) {
result=GetResult(paramObj,storedProcedureName);
} [ ... ]
}
}
torsdag 6 mars 14
Dynamic name
resolution
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Dynamic;
Configuration;
Extensions;
GetAll()
GetPeopleByRole()
x 500
”GetAll”
”GetPeopleByRole”
x 500
namespace Core {
public class Dispatcher<T>:DynamicObject where T:class, new() {
[ ... ]
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args,
out object result) {
string procedureName=binder.Name;
object paramObj=GetParameterObject(binder,args);
string storedProcedureName=GenericDAO<T>.StoredProcedureFullName(procedureName);
if(procedureName.StartsWith("Get")) {
result=GetResult(paramObj,storedProcedureName);
} [ ... ]
}
}
dynamic dao = GenericDAO<Domain.Person>.Dispatch();
IEnumerable<Domain.Person> people = dao.GetPeopleByRole(RoleId: 3);
people.Should().NotBeEmpty();
torsdag 6 mars 14
Dynamic name
resolution
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Dynamic;
Configuration;
Extensions;
GetAll()
GetPeopleByRole()
x 500
”GetAll”
”GetPeopleByRole”
x 500
namespace Core {
public class Dispatcher<T>:DynamicObject where T:class, new() {
[ ... ]
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args,
out object result) {
string procedureName=binder.Name;
object paramObj=GetParameterObject(binder,args);
string storedProcedureName=GenericDAO<T>.StoredProcedureFullName(procedureName);
if(procedureName.StartsWith("Get")) {
result=GetResult(paramObj,storedProcedureName);
} [ ... ]
}
}
dynamic dao = GenericDAO<Domain.Person>.Dispatch();
IEnumerable<Domain.Person> people = dao.GetPeopleByRole(RoleId: 3);
people.Should().NotBeEmpty();
torsdag 6 mars 14
Generated proxy classes
protected internal T CreateObject<T>(IDataReader reader,
IEnumerable<string> allowedFields=null) where T:class, new() {
T proxy=ProxyGenerator.CreateClassProxy<T>(ProxygenerationOptions,
new LazyLoadingInterceptor<T>());
foreach(var fieldAndProperty in GetFieldsToPropertiesMap<T>(reader,
allowedFields)) {
var value=GetValue(reader[fieldAndProperty.Key],
fieldAndProperty.Value);
if(value!=null) {
GetSetter(fieldAndProperty.Value)(proxy,
value);
}
}
return proxy;
}
torsdag 6 mars 14
Generated proxy classes
protected internal T CreateObject<T>(IDataReader reader,
IEnumerable<string> allowedFields=null) where T:class, new() {
T proxy=ProxyGenerator.CreateClassProxy<T>(ProxygenerationOptions,
new LazyLoadingInterceptor<T>());
foreach(var fieldAndProperty in GetFieldsToPropertiesMap<T>(reader,
allowedFields)) {
var value=GetValue(reader[fieldAndProperty.Key],
fieldAndProperty.Value);
if(value!=null) {
GetSetter(fieldAndProperty.Value)(proxy,
value);
}
}
return proxy;
}
torsdag 6 mars 14
Generated proxy classes
namespace Support {
public class LazyLoadingInterceptor<T>:BaseInterceptor,IInterceptor where T:class, new() {
[ ... ]
public void Intercept(IInvocation invocation) {
object proxy=invocation.Proxy;
// Get the target property access method
var target=invocation.MethodInvocationTarget;
string propertyName=GetPropertyName(target.Name);
if(target.Name.StartsWith("get_")) {
if(!IsPropertyLoaded(propertyName)) {
// Ignore the returned enumeration of elements from Prefetcher<T> as it is just the original sequence with properties set
Prefetcher<T>.FetchRelatedProperty(new List<T>() { proxy as T },typeof(T).GetProperty(propertyName));
SetPropertyLoaded(propertyName);
}
} else {
// Setter invocation: update "property loaded" map with indication of whether non-default value set
var setterValue=invocation.GetArgumentValue(0);
SetPropertyLoaded(propertyName,setterValue!=setterValue.GetType().DefaultValue());
}
invocation.Proceed();
}
}
}
torsdag 6 mars 14
Generated proxy classes
namespace Support {
public class LazyLoadingInterceptor<T>:BaseInterceptor,IInterceptor where T:class, new() {
[ ... ]
public void Intercept(IInvocation invocation) {
object proxy=invocation.Proxy;
// Get the target property access method
var target=invocation.MethodInvocationTarget;
string propertyName=GetPropertyName(target.Name);
if(target.Name.StartsWith("get_")) {
if(!IsPropertyLoaded(propertyName)) {
// Ignore the returned enumeration of elements from Prefetcher<T> as it is just the original sequence with properties set
Prefetcher<T>.FetchRelatedProperty(new List<T>() { proxy as T },typeof(T).GetProperty(propertyName));
SetPropertyLoaded(propertyName);
}
} else {
// Setter invocation: update "property loaded" map with indication of whether non-default value set
var setterValue=invocation.GetArgumentValue(0);
SetPropertyLoaded(propertyName,setterValue!=setterValue.GetType().DefaultValue());
}
invocation.Proceed();
}
}
}
dynamically intercepts method invocations
so that properties that are not loaded are fetched
from the database
Intercept
torsdag 6 mars 14
Markup and
configuration
torsdag 6 mars 14
"
"
"
"
public String Name { get; set;}
public String Age { get; set;}
"
"
"
"
"
"
[SP("emp_GetEmployment")]
[ForeignKey("PersonId")]
public virtual ICollection<Employment> Employments { get; set;}
Markup and
configuration
torsdag 6 mars 14
"
"
"
"
public String Name { get; set;}
public String Age { get; set;}
"
"
"
"
"
"
[SP("emp_GetEmployment")]
[ForeignKey("PersonId")]
public virtual ICollection<Employment> Employments { get; set;}
Markup and
configuration
torsdag 6 mars 14
"
"
"
"
public String Name { get; set;}
public String Age { get; set;}
"
"
"
"
"
"
[SP("emp_GetEmployment")]
[ForeignKey("PersonId")]
public virtual ICollection<Employment> Employments { get; set;}
Markup and
configuration
torsdag 6 mars 14
"
"
"
"
public String Name { get; set;}
public String Age { get; set;}
"
"
"
"
"
"
[SP("emp_GetEmployment")]
[ForeignKey("PersonId")]
public virtual ICollection<Employment> Employments { get; set;}
Markup and
configuration
GenericDAO<Person>.Configure("per_GetPeopleWithAddress").By(x => {
x.ScanForRelatedTypes(GenericDAO.FetchRelatedObjectsPolicy.ScanFields);
});
Scan result set for field names that match properties of
related objects
torsdag 6 mars 14
Markup and
configuration
GenericDAO<Person>.Configure("GetPeopleByRole").By(x => {
x.Map("PerId").To(p => p.Id);
x.Include<ContactInfo>().By(y => {
y.Map("ContactPrivateId").To(c => c.Id);
y.Map("PrivateMobile").To(c => c.Mobile);
y.Map("PrivateEmail").To(c => c.Email);
y.Through(p => p.PrivateContact);
});
x.Include<ContactInfo>().By(y => {
y.Map("ContactCompanyId").To(c => c.Id);
y.Map("CompanyMobile").To(c => c.Mobile);
y.Map("CompanyEmail").To(c => c.Email);
y.Through(p => p.CompanyContact);
});
x.Include<Address>();
});
Map each row in the result set to a main Person
object, 2x ContactInfo and an Address
torsdag 6 mars 14
Markup and
configuration
per_GetPersonsByRole()
torsdag 6 mars 14
Markup and
configuration
per_GetPersonsByRole()
PerId
ContactPrivateId
PrivateMobile
PrivateEmail
ContactCompanyId
CompanyMobile
CompanyEmail
3
14
070-123456
john@apple.com
15
073-567890
apple@john.com
torsdag 6 mars 14
Id
StreetAddress
City
20
1 Infinite loop
Götene
Markup and
configuration
per_GetPersonsByRole()
PerId
ContactPrivateId
PrivateMobile
PrivateEmail
ContactCompanyId
CompanyMobile
CompanyEmail
3
14
070-123456
john@apple.com
15
073-567890
apple@john.com
Id
StreetAddress
City
20
1 Infinite loop
Götene
ContactInfo
Id:14
Mobile: "070-123456"
Email: "john@apple.com"
Person
Id: 3
PrivateContact
CompanyContact
Address
ContactInfo
Id: 15
Mobile: "073-567890"
Email: "apple@john.com"
Address
Id: 20
StreetAddress: "1 Infinite loop"
City: "Götene"
torsdag 6 mars 14
Markup and
configuration
per_GetPersonsByRole()
PerId
ContactPrivateId
PrivateMobile
PrivateEmail
ContactCompanyId
CompanyMobile
CompanyEmail
3
14
070-123456
john@apple.com
15
073-567890
apple@john.com
Id
StreetAddress
City
20
1 Infinite loop
Götene
ContactInfo
Id:14
Mobile: "070-123456"
Email: "john@apple.com"
Person
Id: 3
PrivateContact
CompanyContact
Address
ContactInfo
Id: 15
Mobile: "073-567890"
Email: "apple@john.com"
Address
Id: 20
StreetAddress: "1 Infinite loop"
City: "Götene"
torsdag 6 mars 14
Markup and
configuration
y.Map("ContactCompanyId").To(c => c.Id);
torsdag 6 mars 14
Markup and
configuration
y.Map("ContactCompanyId").To(c => c.Id);
public void To<T1>(Expression<Func<T,T1>> propertySelector)
{
var selectorExpression = (MemberExpression) propertySelector.Body;
var prop = (PropertyInfo) selectorExpression.Member;
Configurator.CustomFieldsToPropertiesMap[FieldName]=prop;
}
torsdag 6 mars 14
Markup and
configuration
y.Map("ContactCompanyId").To(c => c.Id);
public void To<T1>(Expression<Func<T,T1>> propertySelector)
{
var selectorExpression = (MemberExpression) propertySelector.Body;
var prop = (PropertyInfo) selectorExpression.Member;
Configurator.CustomFieldsToPropertiesMap[FieldName]=prop;
}
FieldName is the name of the field returned in
the result set (ContactCompanyId)
torsdag 6 mars 14
https://github.com/olale/GenericDAO
torsdag 6 mars 14
Expression Generation
http://msdn.microsoft.com/en-us/library/bb882637.aspx
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
torsdag 6 mars 14
Return an expression tree that accepts T and returns a
boolean
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Node that refers to the parameter x
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
Node that refers to the constant false
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
x == arg
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
for all values of arg in args
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
combined with ”||” (OrElse)
torsdag 6 mars 14
public Expression<Func<T,Boolean>> CreateFilterExpression<T>(params T[] args) {
var x = "x";
var paramExp = Expression.Parameter(typeof(T), x);
Expression t = Expression.Constant(false);
return Expression.Lambda<Func<T,Boolean>>(args.Aggregate(t,
"
"
"
"
"
"
"
(expr, arg) =>
"
"
"
"
"
"
"
Expression.OrElse(Expression.Equal(paramExp, Expression.Constant(arg)),
"
"
"
"
"
"
"
"
"
expr)),
"
"
"
"
"
new ParameterExpression[] { paramExp });
}
torsdag 6 mars 14
torsdag 6 mars 14
torsdag 6 mars 14
Download