I'm using Automapper and three-layered architecture in my application. I have a method which takes Linq query (LambdaExpression) as a parameter.
To pass this parameter in another layer I need to remap it. So, I created an ExpressionExtension class, which is responsible for returning remapped Expression:
/// <summary>
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
/// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam>
/// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns>
public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(
this Expression<Func<TSource, TResult>> expression)
{
Contract.Requires(expression != null);
Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null);
var newParameter = Expression.Parameter(typeof(TDestination));
Contract.Assume(newParameter != null);
var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter);
var remappedBody = visitor.Visit(expression.Body);
if (remappedBody == null)
{
throw new InvalidOperationException("Unable to remap expression");
}
return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter);
}
}
And code for AutoMapVisitor:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly ParameterExpression _newParameter;
private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_newParameter = newParameter;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var propertyMaps = _typeMap.GetPropertyMaps();
// Find any mapping for this member
var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member);
if (propertyMap == null)
{
return base.VisitMember(node);
}
var destinationProperty = propertyMap.DestinationProperty;
var destinationMember = destinationProperty.MemberInfo;
// Check the new member is a property too
var property = destinationMember as PropertyInfo;
if (property == null)
{
return base.VisitMember(node);
}
// Access the new property
var newPropertyAccess = Expression.Property(_newParameter, property);
return base.VisitMember(newPropertyAccess);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T">Func of type TIn and result is TResult</typeparam>
/// <param name="node">Expression of Func with certain input and result types</param>
/// <returns></returns>
protected override Expression VisitLambda<T>(Expression<T> node)
{
//predicate.RemapForType<PersonViewModel, PersonDTO, bool>()
// считаем, что будет 1 параметр(объект) операции
var source = node.Parameters.FirstOrDefault();
// берем первый найденный маппинг источника
TypeMap typeMap = source == null
? null
: Mapper.GetAllTypeMaps().FirstOrDefault(tMap => tMap.SourceType == source.Type);
if (typeMap!=null)
{
var ts = typeMap.SourceType;
var td = typeMap.DestinationType;
var res = node.ReturnType;
return VisitLambda(node, ts, td, res);
//return node.RemapForType<ts,td,res>();
}
return base.VisitLambda<T>(node);
}
private Expression<Func<TOut,TResult>> VisitLambda<T,TIn, TOut, TResult>(Expression<T> node, TIn source, TOut destination, TResult result)
{
ParameterExpression pe = Expression.Parameter(typeof (TIn), "innerParam");
var value = typeof(T) as Func<TIn, TResult>;
Expression<Func<TIn, TResult>> input = Expression.Lambda<Func<TIn, TResult>>(Expression.Invoke(node.Body,node.Parameters)); // tried also without Expression.Invoke
return input.RemapForType<TIn, TOut, TResult>();
}
}
I understand that I need to override base Lambda Visitor and to call ExpressionExtension.RemapForType method once again to remap inner lambda. But how can I pass the right parameters to it? So, I decided to create generic method but nothing works right.
Could you help me to get the right solution.
E.g. something like that:
Expression<Func<TSource,TResult>> a = val => val.FullName == "ABC" && val.SomeEnumerableProperty.Any(inner => inner.City == "Moscow");
// Here we will get remapped lambda with TDestination class as a parameter and TDestination class properties remapped by AutoMapper as you saw in my posted code
Expression<Func<TDestination, TResult>> b = a.RemapForType<TSource,TDestination,TResult>();
And in I don't know which classes will be used as Source and Destination in inner Lambda because it is runtime. So the example is simply for you to understand what I want to do.
EDITED
So, I changed my Visitor in this variant:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly TypeMap _typeMap;
private readonly List<TypeMap> _typeMaps = new List<TypeMap>();
private readonly Dictionary<Type, ParameterExpression> parameterMap = new Dictionary<Type, ParameterExpression>();
private Dictionary<MemberInfo, Expression> memberMap = new Dictionary<MemberInfo, Expression>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
Contract.Assume(_typeMap != null);
_typeMaps.Add(_typeMap);
_typeMaps.AddRange(FillNestedTypeMaps(_typeMap));
parameterMap.Add(newParameter.Type, newParameter); // main parameter which we don't need to recreate
foreach (TypeMap map in _typeMaps)
{
if (parameterMap.ContainsKey(map.DestinationType)) continue;
parameterMap.Add(map.DestinationType, Expression.Parameter(map.DestinationType, map.DestinationType.Name.ToLower()));
}
}
/// <summary>
/// находим все возможные маппинги текущей конфигурации вниз по иерархии свойств класса источника
/// </summary>
/// <param name="tMap">Объект конфигурации, содержащий информацию о типе источника и типе назначения</param>
private IEnumerable<TypeMap> FillNestedTypeMaps(TypeMap tMap)
{
List<TypeMap> result = new List<TypeMap>();
// 1 where: маппинг только между классами
// 2 where: маппинг не равен входящему и ReverseMap значению, например на входе: A -> B, тогда отпадут значения A -> B и B -> A из проверки
// 3 where: берем те свойства, тип которых совпадает с входящим типом источника или, если это обобщенный тип, с его аргументом и тоже самое для типа назначения
IEnumerable<PropertyMap> pMaps = tMap.GetPropertyMaps();
var list = Mapper.GetAllTypeMaps()
.Where(
map => map.SourceType.IsClass && map.DestinationType.IsClass)
.Where(map =>
!(map.Equals(tMap) ||
(map.SourceType == tMap.DestinationType && map.DestinationType == tMap.SourceType)))
.Where(
map =>
pMaps
.Any(
pi =>
{
var pis = pi.SourceMember as PropertyInfo;
if (pis == null) return false;
bool forSource = pis.PropertyType == map.SourceType ||
(pis.PropertyType.IsGenericType &&
pis.PropertyType.GetGenericArguments()[0] == map.SourceType);
bool forDestination = pi.DestinationPropertyType == map.DestinationType ||
(pi.DestinationPropertyType.IsGenericType &&
pi.DestinationPropertyType.GetGenericArguments()[0] == map.DestinationType);
return forSource && forDestination;
}))
.ToList();
if (list.Count > 0)
{
result.AddRange(list);
foreach (TypeMap typeMap in list)
{
result.AddRange(FillNestedTypeMaps(typeMap));
}
}
return result;
}
private Type Map(Type type)
{
var tMap = _typeMaps.FirstOrDefault(map => map.SourceType == type);
Contract.Assume(tMap != null);
return tMap.DestinationType;
}
private ParameterExpression Map(ParameterExpression parameter)
{
var mappedType = Map(parameter.Type);
ParameterExpression mappedParameter;
if (!parameterMap.TryGetValue(mappedType, out mappedParameter))
parameterMap.Add(mappedType, mappedParameter = Expression.Parameter(mappedType, parameter.Name));
return mappedParameter;
}
private Expression Map(MemberInfo mi, Expression exp)
{
Expression val;
if (!memberMap.TryGetValue(mi, out val))
{
foreach (PropertyMap propertyMap in
_typeMaps.Select(map => map.GetPropertyMaps().SingleOrDefault(m => m.SourceMember == mi))
.Where(propertyMap => propertyMap != null))
{
memberMap.Add(mi, val = Expression.PropertyOrField(exp, propertyMap.DestinationProperty.MemberInfo.Name));
break;
}
}
return val;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var expression = Visit(node.Expression);
if (expression == node.Expression)
return node;
return Map(node.Member, expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(Visit(node.Body), node.Parameters.Select(Map));
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Map(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// if static object and generic method
if (node.Object == null && node.Method.IsGenericMethod)
{
// Static generic method
var args = Visit(node.Arguments);
var genericArgs = node.Method.GetGenericArguments().Select(Map).ToArray();
var method = node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgs);
return Expression.Call(method, args);
}
return base.VisitMethodCall(node);
}
}
And now after remapping it throws an Exception:
variable 'person' of type 'Reestr.DAL.Entities.Person' referenced from scope '', but it is not defined
It is interesting that this Exception is thrown only when we have an inner Lambda (e.g. method Any). But if we didn't include it, using only simple search conditions, everything works fine! Seems like there is missing reference on Enumerable property of mapped class or there is newly created parameter somewhere and I can't understand where it can be.
Could you give me any suggestions, please, how can I solve this issue! Thanks!
EDITED
As #MBoros mentioned Automapper supports such mapping out of the box (of course if there are no hard code sections). The problem is in ServiceStack.OrmLite!!! I've tried simply passing manually created query with inner subquery and it failed with an exception I've posted earlier...
Related
I am trying to perform a SQL "Like" wildcard search using LINQ 2 SQL with the below query
public IQueryable<DbHeader> GetHeaders()
{
return Work.Context.Headers
.Include(m => m.Terms.Select(t => t.MetaDataPairs))
.Include(m => m.States)
.Include(m => m.Attachments);
}
var dbt = from th in _objectStoreRepository.GetHeaders()
where SqlMethods.Like(th.CreatedBy, "test")
select th;
foreach (var t in dbt) {
...
However when I run this, on the foreach line I get
An unexpected error occurred: LINQ to Entities does not recognize the
method 'Boolean Like(System.String, System.String)' method, and this
method cannot be translated into a store expression.
Why is it considering it a LINQ to Entities query? I thought this was pure LINQ 2 SQL
to do what you want .. i usually use this Utlis .. (extension method that perform a wherelike with wildcards),.... hope it heps you
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Fwd.Repository.EF.Utils
{
public static class LinqUtils
{
/// <summary>
/// Wheres the like.
/// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <param name="source">The source.</param>
/// <param name="valueSelector">The value selector.</param>
/// <param name="value">The value.</param>
/// <param name="wildcard">The wildcard.</param>
/// <returns></returns>
public static IQueryable<TSource> WhereLike<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, string>> valueSelector, string value, char wildcard)
{
return source.Where(BuildLikeExpression(valueSelector, value, wildcard));
}
/// <summary>
/// Builds the like expression.
/// </summary>
/// <typeparam name="TElement">The type of the element.</typeparam>
/// <param name="valueSelector">The value selector.</param>
/// <param name="value">The value.</param>
/// <param name="wildcard">The wildcard.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">valueSelector</exception>
public static Expression<Func<TElement, bool>> BuildLikeExpression<TElement>(
Expression<Func<TElement, string>> valueSelector, string value, char wildcard)
{
if (valueSelector == null)
throw new ArgumentNullException("valueSelector");
var method = GetLikeMethod(value, wildcard);
value = value.Trim(wildcard);
var body = Expression.Call(valueSelector.Body, method, Expression.Constant(value));
var parameter = valueSelector.Parameters.Single();
return Expression.Lambda<Func<TElement, bool>>(body, parameter);
}
/// <summary>
/// Gets the like method.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="wildcard">The wildcard.</param>
/// <returns></returns>
private static MethodInfo GetLikeMethod(string value, char wildcard)
{
var methodName = "Contains";
var textLength = value.Length;
value = value.TrimEnd(wildcard);
if (textLength > value.Length)
{
methodName = "StartsWith";
textLength = value.Length;
}
value = value.TrimStart(wildcard);
if (textLength > value.Length)
{
methodName = (methodName == "StartsWith") ? "Contains" : "EndsWith";
textLength = value.Length;
}
var stringType = typeof(string);
return stringType.GetMethod(methodName, new Type[] { stringType });
}
}
}
then you can use it on your IQueryable Entities like
var query = _dbContext.Users.WhereLike(xx=>xx.name,"fred*","*")
As the error message says, it's not Linq to SQL but Linq to Entities where the SqlMethods class is not supported (see the Microsoft documentation). I guess, it's because you run your query against an ObjectContext or DbContext of the Entity Framework.
On Linq to Entities the following will work:
var dbt = from th in _objectStoreRepository.GetHeaders()
where th.CreatedBy.Contains("test")
select th;
I am trying to get a static field info into metro app and I don't find a way to do that.
I have tried:
- type.GetRuntimeField
- typeInfo.GetDeclaredField in a loop to delve into every parent types
/// <summary>
/// Gets the field info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>The field info if found, null otherwise</returns>
public static FieldInfo GetField(this Type type, string fieldName)
{
var currentType = type;
FieldInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredField(fieldName);
currentType = typeInfo.BaseType;
}
return result;
}
... am I missing something or is there anyway to get a static field on a type using reflection in metro app?....
edit:
Well, I am so sorry for those who have waste time on this question, Dependency properties defined in the framework are actualy not readonly static fields, they are static properties... As I usualy declare my dps as field, I didn't consider the fact that form example FrameworkElement.Width could be a property...
So here is the code I used to get fields and property info:
public static class TypeExtensions
{
/// <summary>
/// Gets the field info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>The field info if found, null otherwise</returns>
public static FieldInfo GetField(this Type type, string fieldName)
{
var currentType = type;
FieldInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredField(fieldName);
currentType = typeInfo.BaseType;
}
return result;
}
/// <summary>
/// Gets the property info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="propertyName">The name of the property</param>
/// <returns>The field info if found, null otherwise</returns>
public static PropertyInfo GetProperty(this Type type, string propertyName)
{
var currentType = type;
PropertyInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredProperty(propertyName);
currentType = typeInfo.BaseType;
}
return result;
}
}
public static class DependencyObjectExtensions
{
public static DependencyProperty GetDependencyProperty(this DependencyObject dependencyObject, string propertyName)
{
var dependencyPropertyName = propertyName + "Property";
var type = dependencyObject.GetType();
var fieldInfo = type.GetField(dependencyPropertyName);
if (fieldInfo == null)
{
var propertyInfo = type.GetProperty(dependencyPropertyName);
if (propertyInfo != null)
{
return propertyInfo.GetValue(dependencyObject) as DependencyProperty;
}
}
else
{
var value = fieldInfo.GetValue(dependencyObject);
return value as DependencyProperty;
}
return null;
}
}
Thanks a lot
Regards,
Charles
I have the below in my controller and would like to pass x, y, and z of type float to subscribers. I'm having some difficulties catching on however. What would I have to adjust to allow me to pass my floats (x, y, z) as parameters? Thank you.
private void ProcessEvents()
{
if(Input.GetMouseButtonDown(1))
{
// Data to be passed to subscribers
float x = Input.mousePosition.x;
float y = Input.mousePosition.y;
float z = Input.mousePosition.z;
var publisher = new EventPublisher();
var handler = new Handler();
// Void delegate with one parameter
string eventName = "RightClickEvent";
var rightClickEvent = publisher.GetType().GetEvent(eventName);
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<int>(rightClickEvent, i=>Debug.LogError(i + "!")));
publisher.PublishEvents();
}
}
Other sources:
public class ExampleEventArgs : EventArgs
{
public int IntArg {get; set;}
}
Event Publisher:
public class EventPublisher
{
public event EventHandler<ExampleEventArgs> RightClickEvent;
/// <summary>
/// Publishes the events.
/// </summary>
public void PublishEvents()
{
if(RightClickEvent != null)
{
RightClickEvent(this, new ExampleEventArgs{IntArg = 5});
}
}
}
Handler:
public class Handler
{
public void HandleEventWithArg(int arg) { Debug.LogError("Arg: " + string.Format("[{0}]", arg)); }
}
EventProxy:
static class EventProxy
{
// Void delegate with one parameter
static public Delegate Create<T>(EventInfo evt, Action<T> d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg)
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body,parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
// Returns an expression that represents an argument to be passed to the delegate
static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if(eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return Expression.MakeMemberAccess(eventArgs,memberInfo);
}
throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
// Void delegates with no parameters
static public Delegate Create(EventInfo evt, Action d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, EventArgs x1) => d()
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x"));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"));
var lambda = Expression.Lambda(body,parameters.ToArray());
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
}
Ok, so i read what you said about decoupling application modules in MVC-like style. I normally like to work with strong typed code, even when using reflection, but i'm relatively new to MVC, and don't know the recommended practices. You know your requirements better than i do, so i simply edited Nguyen's solution, because i believe he was using some extensions that were not included in the file, and posted the result here. All credit goes to Nguyen.
namespace Dynamics
{
public static class DynamicHandler
{
/// <summary>
/// Invokes a static delegate using supplied parameters.
/// </summary>
/// <param name="targetType">The type where the delegate belongs to.</param>
/// <param name="delegateName">The field name of the delegate.</param>
/// <param name="parameters">The parameters used to invoke the delegate.</param>
/// <returns>The return value of the invocation.</returns>
public static object InvokeDelegate(this Type targetType, string delegateName, params object[] parameters)
{
return ((Delegate)targetType.GetField(delegateName).GetValue(null)).DynamicInvoke(parameters);
}
/// <summary>
/// Invokes an instance delegate using supplied parameters.
/// </summary>
/// <param name="target">The object where the delegate belongs to.</param>
/// <param name="delegateName">The field name of the delegate.</param>
/// <param name="parameters">The parameters used to invoke the delegate.</param>
/// <returns>The return value of the invocation.</returns>
public static object InvokeDelegate(this object target, string delegateName, params object[] parameters)
{
return ((Delegate)target.GetType().GetField(delegateName).GetValue(target)).DynamicInvoke(parameters);
}
/// <summary>
/// Adds a dynamic handler for a static delegate.
/// </summary>
/// <param name="targetType">The type where the delegate belongs to.</param>
/// <param name="fieldName">The field name of the delegate.</param>
/// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AddHandler(this Type targetType, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(targetType, fieldName, func, null, false);
}
/// <summary>
/// Adds a dynamic handler for an instance delegate.
/// </summary>
/// <param name="target">The object where the delegate belongs to.</param>
/// <param name="fieldName">The field name of the delegate.</param>
/// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AddHandler(this object target, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(target.GetType(), fieldName, func, target, false);
}
/// <summary>
/// Assigns a dynamic handler for a static delegate or event.
/// </summary>
/// <param name="targetType">The type where the delegate or event belongs to.</param>
/// <param name="fieldName">The field name of the delegate or event.</param>
/// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AssignHandler(this Type targetType, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(targetType, fieldName, func, null, true);
}
/// <summary>
/// Assigns a dynamic handler for a static delegate or event.
/// </summary>
/// <param name="target">The object where the delegate or event belongs to.</param>
/// <param name="fieldName">The field name of the delegate or event.</param>
/// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AssignHandler(this object target, string fieldName, Func<object[], object> func)
{
return InternalAddHandler(target.GetType(), fieldName, func, target, true);
}
private static Type InternalAddHandler(Type targetType, string fieldName,
Func<object[], object> func, object target, bool assignHandler)
{
Type delegateType;
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic |
(target == null ? BindingFlags.Static : BindingFlags.Instance);
var eventInfo = targetType.GetEvent(fieldName, bindingFlags);
if (eventInfo != null && assignHandler)
throw new ArgumentException("Event can be assigned. Use AddHandler() overloads instead.");
if (eventInfo != null)
{
delegateType = eventInfo.EventHandlerType;
var dynamicHandler = BuildDynamicHandler(delegateType, func);
eventInfo.GetAddMethod(true).Invoke(target, new Object[] { dynamicHandler });
}
else
{
var fieldInfo = targetType.GetField(fieldName);
//,target == null ? BindingFlags.Static : BindingFlags.Instance);
delegateType = fieldInfo.FieldType;
var dynamicHandler = BuildDynamicHandler(delegateType, func);
var field = assignHandler ? null : target == null
? (Delegate)fieldInfo.GetValue(null)
: (Delegate)fieldInfo.GetValue(target);
field = field == null
? dynamicHandler
: Delegate.Combine(field, dynamicHandler);
if (target != null)
target.GetType().GetField(fieldName).SetValue(target, field);
else
targetType.GetField(fieldName).SetValue(null, field);
//(target ?? targetType).SetFieldValue(fieldName, field);
}
return delegateType;
}
/// <summary>
/// Dynamically generates code for a method whose can be used to handle a delegate of type
/// <paramref name="delegateType"/>. The generated method will forward the call to the
/// supplied <paramref name="func"/>.
/// </summary>
/// <param name="delegateType">The delegate type whose dynamic handler is to be built.</param>
/// <param name="func">The function which will be forwarded the call whenever the generated
/// handler is invoked.</param>
/// <returns></returns>
public static Delegate BuildDynamicHandler(this Type delegateType, Func<object[], object> func)
{
var invokeMethod = delegateType.GetMethod("Invoke");
var parameters = invokeMethod.GetParameters().Select(parm =>
Expression.Parameter(parm.ParameterType, parm.Name)).ToArray();
var instance = func.Target == null ? null : Expression.Constant(func.Target);
var convertedParameters = parameters.Select(parm => Expression.Convert(parm, typeof(object))).Cast<Expression>().ToArray();
var call = Expression.Call(instance, func.Method, Expression.NewArrayInit(typeof(object), convertedParameters));
var body = invokeMethod.ReturnType == typeof(void)
? (Expression)call
: Expression.Convert(call, invokeMethod.ReturnType);
var expr = Expression.Lambda(delegateType, body, parameters);
return expr.Compile();
}
}
}
And i also added some code to test the methods, i could have just used simple lambdas when i assigned the callback delegates, but i rather use the "return true" definitions because i set breakpoints to check that the functions are actually called.
class TestClass
{
private void Test()
{
TestInstance();
TestStatic();
}
private void TestInstance()
{
var eventClass = new EventClass();
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
eventClass.AddHandler("InstanceEvent", parameters =>
{
return true;
});
eventClass.AddHandler("InstanceEventRaiseDelegate", parameters =>
{
return true;
});
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
eventClass.AssignHandler("InstanceEventRaiseDelegate", parameters =>
{
return true;
});
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
}
private void TestStatic()
{
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
typeof(EventClass).AddHandler("StaticEvent", parameters =>
{
return true;
});
typeof(EventClass).AddHandler("StaticEventRaiseDelegate", parameters =>
{
return true;
});
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
typeof(EventClass).AssignHandler("StaticEventRaiseDelegate", parameters =>
{
return true;
});
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
}
public class EventClass
{
#region Instance
public EventClass()
{
InstanceEventRaiseDelegate = OnInstanceEvent;
}
public Action InstanceEventRaiseDelegate;
public event EventHandler InstanceEvent;
public void OnInstanceEvent()
{
if (InstanceEvent != null)
InstanceEvent(this, EventArgs.Empty);
}
#endregion
#region Static
static EventClass()
{
StaticEventRaiseDelegate = OnStaticEvent;
}
public static Action StaticEventRaiseDelegate;
public static event EventHandler StaticEvent;
public static void OnStaticEvent()
{
if (StaticEvent != null)
StaticEvent(null, EventArgs.Empty);
}
#endregion
}
}
Sorry for the late response, but it seems you were able to find the solution elsewhere :), goodluck.
I have added some code that will achieve what u require:
I modified the getArgExpression function to this
// Returns a List of expressions that represent the arguments to be passed to the delegate
static IEnumerable<Expression> getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if (eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return new List<Expression> { Expression.MakeMemberAccess(eventArgs, memberInfo) };
}
if (eventArgs.Type == typeof(MousePositionEventArgs) && handlerArgType == typeof(float))
{
//"x1.X"
var xMemberInfo = eventArgs.Type.GetMember("X")[0];
//"x1.Y"
var yMemberInfo = eventArgs.Type.GetMember("Y")[0];
//"x1.Z"
var zMemberInfo = eventArgs.Type.GetMember("Z")[0];
return new List<Expression>
{
Expression.MakeMemberAccess(eventArgs, xMemberInfo),
Expression.MakeMemberAccess(eventArgs, yMemberInfo),
Expression.MakeMemberAccess(eventArgs, zMemberInfo),
};
}
throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
The calling function stays the same, because it has an overload for IEnumerable<Expression>.
I then added the EventArgs specific to your MousePosition situation
public class MousePositionEventArgs : EventArgs
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
Further more i wrote a new function in 'EventProxy' that will handle a delegate with 3 parameters of the same type
// Void delegate with three parameters
static public Delegate Create<T>(EventInfo eventInformation, Action<T, T, T> lambdaDelegate)
{
var handlerType = eventInformation.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.X,x1.Y,x1.Z)
var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(lambdaDelegate), lambdaDelegate.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body, parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
Now we can easily add a MouseEvent subscription by using the following code
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<float>(rightClickEvent, (t, u, v) => Console.Write(t + "x" + u + "x" + v + "!")));
Design-wise this solution is not the best, it uses the current design you have shown, but i do have strong reservations as to maintaining a scalable and easy to follow architecture with this approach.
I would suggest you create an adapter/converter that will generate the lambda parameters from the EventArgs class, they can be easily specified in the EventArgs by implementing an interface, that way the getArgExpression will only use the converter to generate the appropriate parameters, and you can get rid of the many if (Type == typeof(...)).
It will take me some more time to draw up that solution, but if you are really interested, i can try to write it down, and post it.
I'm setting up a generic repository in my application and I'm having troubles with some LINQ queries.
With a non-generic one, this is what I would do:
private IObjectSet<T> _objectSet;
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
var r = (from n in _objectSet
select n.Group_ID).Distinct();
objectGroups = r.OrderBy(n => n.Value).ToList();
foreach (object value in objectGroups)
{
intGroups.Add((int?)value);
}
return intGroups;
}
Since this is a generic one of type T, when typing "n.", IntelliSense is not listing any options since the type of n is not explicitly defined (right?).
So here's what I have so far:
public interface IRepository<T> : IDisposable where T : class
{
IQueryable<T> Fetch();
IEnumerable<T> GetAll();
IEnumerable<T> GetAll(bool activeOnly);
IEnumerable<T> GetAll(string groupID, bool activeOnly);
IEnumerable<T> Find(Func<T, bool> predicate);
T Single(Func<T, bool> predicate);
T First(Func<T, bool> predicate);
List<int?> GetGroups();
int Add(T entity);
void Delete(T entity);
void Attach(T entity);
void SaveChanges();
void SaveChanges(SaveOptions options);
}
public class DataRepository<T> : IRepository<T> where T : class
{
/// <summary>
/// The context object for the database
/// </summary>
private ObjectContext _context;
private IEnumerable<T> _previousEntries;
private string _PKName;
/// <summary>
/// The IObjectSet that represents the current entity.
/// </summary>
private IObjectSet<T> _objectSet;
public DataRepository()
{
switch (typeof(T).Name)
{
case "CRM_Patient":
_context = new TheseEntities();
_PKName = "key_patient";
break;
case "CRM_Account":
_context = new ThoseEntities();
_PKName = "accountnumber";
break;
case "CRM_Supplier":
_context = new OtherEntities();
_PKName = "supplierid";
break;
default:
_context = new OtherEntities();
break;
}
_objectSet = _context.CreateObjectSet<T>();
_previousEntries = this.GetAll();
}
/// <summary>
/// Gets all records as an IQueryable
/// </summary>
/// <returns>An IQueryable object containing the results of the query</returns>
public IQueryable<T> Fetch()
{
return _objectSet;
}
/// <summary>
/// Gets all records as an IEnumberable
/// </summary>
/// <returns>An IEnumberable object containing the results of the query</returns>
public IEnumerable<T> GetAll()
{
return Fetch().AsEnumerable();
}
/// <summary>
/// Finds a record with the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A collection containing the results of the query</returns>
public IEnumerable<T> Find(Func<T, bool> predicate)
{
return _objectSet.Where<T>(predicate);
}
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
//var r = (from n in _objectSet
// select n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
var r = Fetch().Select(n => n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
objectGroups = r.OrderBy(n => n.GetType().GetProperty("Value").GetValue(n, null)).ToList();
foreach (object value in r)
{
intGroups.Add((int?)value);
}
return intGroups;
}
/// <summary>
/// Gets a single record by the specified criteria (usually the unique identifier)
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record that matches the specified criteria</returns>
public T Single(Func<T, bool> predicate)
{
return _objectSet.Single<T>(predicate);
}
/// <summary>
/// The first record matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record containing the first record matching the specified criteria</returns>
public T First(Func<T, bool> predicate)
{
return _objectSet.First<T>(predicate);
}
/// <summary>
/// Deletes the specified entitiy
/// </summary>
/// <param name="entity">Entity to delete</param>
/// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
public void Delete(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.DeleteObject(entity);
}
/// <summary>
/// Deletes records matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
public void Delete(Func<T, bool> predicate)
{
IEnumerable<T> records = from x in _objectSet.Where<T>(predicate) select x;
foreach (T record in records)
{
_objectSet.DeleteObject(record);
}
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
Somehow, my reflection tricks seem to be executed in the LINQ statements, which obviously ends up throwing an exception when I assign a value for objectGroups.
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object, System.Object[])' method, and this method cannot be translated into a store expression.
Any ideas ? I really need to preserve the generic nature of the repository. All of the methods where I'm using Reflection are throwing exceptions...
Thanks much.
EDIT: Added most of the Repository's methods and the interface. Some methods might be missing from the class but done so to lighten reading :)
How about defining the Value and GroupID properties in an interface, and then making your entities implement this interface and adding this as a generic constraint?
Then your GetGroups method will be able to call these properties without reflection.
I know I can call linq's Except and specify a custom IEqualityComparer, but implementing a new Comparer class for each data type seems like an overkill for this purpose. Can I use a lambda expression to provide the equality function, like when I use Where or other LINQ functions?
If I can't, is there an alternative?
For any one still looking; here's another way of implementing a custom lambda comparer.
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _expression;
public LambdaComparer(Func<T, T, bool> lambda)
{
_expression = lambda;
}
public bool Equals(T x, T y)
{
return _expression(x, y);
}
public int GetHashCode(T obj)
{
/*
If you just return 0 for the hash the Equals comparer will kick in.
The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects),
you will always fall through to the Equals check which is what we are always going for.
*/
return 0;
}
}
you can then create an extension for the linq Except an Intersect that take in lambda's
/// <summary>
/// Returns all items in the first collection except the ones in the second collection that match the lambda condition
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="listA">The first list</param>
/// <param name="listB">The second list</param>
/// <param name="lambda">The filter expression</param>
/// <returns>The filtered list</returns>
public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda)
{
return listA.Except(listB, new LambdaComparer<T>(lambda));
}
/// <summary>
/// Returns all items in the first collection that intersect the ones in the second collection that match the lambda condition
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="listA">The first list</param>
/// <param name="listB">The second list</param>
/// <param name="lambda">The filter expression</param>
/// <returns>The filtered list</returns>
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda)
{
return listA.Intersect(listB, new LambdaComparer<T>(lambda));
}
Usage:
var availableItems = allItems.Except(filterItems, (p, p1) => p.Id== p1.Id);
Can you not use a .Where with a lambda that filters out your required values?
Example as requested:
static void Main(string[] args)
{
var firstCustomers = new[] { new Customer { Id = 1, Name = "Bob" }, new Customer { Id = 2, Name = "Steve" } };
var secondCustomers = new[] { new Customer { Id = 2, Name = "Steve" }, new Customer { Id = 3, Name = "John" } };
var customers = secondCustomers.Where(c => !firstCustomers.Select(fc => fc.Id).Contains(c.Id));
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
I don't think you can directly with the basic LINQ interfaces, but I've seen people implement a LambdaComparer class with extension methods which will help you do it.
Here's an example
Here's something simple I whipped up:
public class CustomComparer<TSource, TCompareType> : IEqualityComparer<TSource> where TSource : class
{
private readonly Func<TSource, TCompareType> getComparisonObject;
public CustomComparer(Func<TSource,TCompareType> getComparisonObject)
{
if (getComparisonObject == null) throw new ArgumentNullException("getComparisonObject");
this.getComparisonObject = getComparisonObject;
}
/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
/// <returns>
/// true if the specified objects are equal; otherwise, false.
/// </returns>
/// <param name="x">The first object of type <paramref name="T"/> to compare.
/// </param><param name="y">The second object of type <paramref name="T"/> to compare.
/// </param>
public bool Equals(TSource x, TSource y)
{
if (x == null)
{
return (y == null);
}
else if (y == null)
{
return false;
}
return EqualityComparer<TCompareType>.Default.Equals(getComparisonObject(x), getComparisonObject(y));
}
/// <summary>
/// Returns a hash code for the specified object.
/// </summary>
/// <returns>
/// A hash code for the specified object.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.
/// </param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
/// </exception>
public int GetHashCode(TSource obj)
{
return EqualityComparer<TCompareType>.Default.GetHashCode(getComparisonObject(obj));
}
}
Usage:
var myItems = allItems.Except(theirItems, new CustomComparer(item => item.Name));
Use an extension!
public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other,
Func<T, TKey> getKey)
{
return from item in items
join otherItem in other on getKey(item)
equals getKey(otherItem) into tempItems
from temp in tempItems.DefaultIfEmpty()
where ReferenceEquals(null, temp) || temp.Equals(default(T))
select item;
}
Source
Here' is l except r solution, based on a LINQ outer join technique:
from l in new[] { 1, 2, 3 }
join r in new[] { 2, 4, 5 }
on l equals r
into rr
where !rr.Any()
select l
Will yield: 1, 3
But you may try this. This is so much easy, I think code contains errors. Of course, the code is for small amounts, without LINQ translates to db etc.
public static IEnumerable<TSource> ExceptPredicate<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> compare) {
foreach (var itmFirst in first) {
if (!second.Any(itmsecond => compare(itmFirst, itmsecond))) {
yield return itmFirst;
}
}
yield break;
}