c# : Issues running hangfire job when creating expression - c#

Traying to create an expression and delegate hangfire to run it
This is my task to be executed
public class MathClass : IBackgroundJob<MatchClassParams>
{
public Task RunJobAsync(MatchClassParams payload)
{
return Task.FromResult(payload.A + payload.B);
}
}
public class MatchClassParams : IBackgroundJobPayload
{
public int A { get; set; }
public int B { get; set; }
}
This is my code
var calculator = new MathClass();
MethodInfo methodInfo = typeof(MathClass).GetMethod("RunJobAsync");
ParameterExpression param = Expression.Parameter(typeof(MatchClassParams), "payload");
var thisParameter = Expression.Constant(calculator);
MethodCallExpression methodCall = Expression.Call(thisParameter, methodInfo, param);
Expression<Func<MatchClassParams, Task>> expression = Expression.Lambda<Func<MatchClassParams, Task>>(
methodCall,
new ParameterExpression[] { param }
);
// IBackgroundJobClient _backgroundJobClient
_backgroundJobClient.Enqueue(lambda);
When hangfire tries to execute the expression I'm getting
variable 'payload' of type 'AnalyseApi.Scheduler.MatchClassParams' referenced from scope '', but it is not defined

Related

Make delegate for method at runtime

I found this interesting article Reflection Performance - Create Delegate (Properties C#)
the described approach works great for properties. So I tried to to make it work for Methods, too, but without success.
Classes / Properties / Methods
public class bmecatContent
{
private bmecatHeader header;
private bmecatCatalog catalog;
private List<bmecatFieldValue> fieldValueList;
public bmecatContent()
{
header = new bmecatHeader();
catalog = new bmecatCatalog();
}
public string DeclarationVersion { get; set; }
public string DeclarationEncoding { get; set; }
public string BmecatVersion { get; set; }
public bmecatHeader Header
{ get { return header; } }
public bmecatCatalog Catalog
{ get { return catalog; } }
}
public class bmecatCatalog
{
private List<bmecatCatalogGroupSystem> catalogGroupSystem;
private List<bmecatClassificationSystem> classificationSystem;
private List<bmecatProduct> products;
private List<bmecatProductToCataloggroupMap> productToCataloggroupMap;
public bmecatCatalog()
{
catalogGroupSystem = new List<bmecatCatalogGroupSystem>();
classificationSystem = new List<bmecatClassificationSystem>();
products = new List<bmecatProduct>();
productToCataloggroupMap = new List<bmecatProductToCataloggroupMap>();
}
public List<bmecatClassificationSystem> Classification_System
{ get { return classificationSystem; } }
public List<bmecatCatalogGroupSystem> Catalog_Group_System
{ get { return catalogGroupSystem; } }
public List<bmecatProduct> Products
{ get { return products; } }
public List<bmecatProductToCataloggroupMap> Product_To_Cataloggroup_Map
{ get { return productToCataloggroupMap; } }
public bmecatProduct GetProductByInernationalPid(string Pid)
{
// linq
var query = from prodItem in products
from innerList in prodItem.Product_Details.International_PID
where innerList.PID == Pid
select prodItem;
return query.FirstOrDefault();
}
}
my current Approach looks like:
// Properties
public static Func<object, object> BuildGetAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
Expression<Func<object, object>> expr =
Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method),
typeof(object)),
obj);
return expr.Compile();
}
// Methods (with string Parameter)
public static Func<object, string, object> BuildMethodAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
var strParam = Expression.Parameter(typeof(string), "strParam");
//var param = method.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).FirstOrDefault();
var param = Expression.Convert(strParam, method.GetParameters().First().ParameterType);
Expression<Func<object, string, object>> expr =
Expression.Lambda<Func<object, string, object>>(
Expression.Convert(Expression.Call(Expression.Convert(obj, method.DeclaringType), method, param),
typeof(object)),
obj);
return expr.Compile();
}
this code generates messages, that for the lambda-declaration a wrong number of Parameters was used.
thx a lot for your help!
// Update
this is my "work in progress" part when it Comes to creating & using the delegates:
bmecatParser parser = new bmecatParser();
// parser contains Property BmecatContent of type bmecatContent
// BmecatContent contains all properties and Methods I Need to Access at runtime
// e.g. BmecatContent.Catalog, BmecatContent.Catalog.GetProductByInernationalPid(string Pid)
// gets instance of main-class
var property = parser.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "BmecatContent");
var access = Extensions.BuildGetAccessor(property.GetGetMethod());
var resultBmecatContent = access(parser);
// gets instance of class that holds method
property = resultBmecatContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "Catalog");
access = Extensions.BuildGetAccessor(property.GetGetMethod());
var resultCatalog = access(resultBmecatContent);
// here I try to get value from method that has 1 Parameter (string)
var method = resultCatalog.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "GetProductByInernationalPid");
var accessProd = Extensions.BuildMethodAccessor(method);
var resultProduct = accessProd(resultCatalog, "4317784548366");
the idea behind this is to parse given classes + properties structure, where user provides propertynames / methodnames within mappinginstructions.

c# reflectin: calling method with generic type and generic parameter for given instance of class

I need to call generic method with generic parameter using reflection, where generic type is supplied as parameter within string. There are some examples online for generics and reflection, but I still cannot get this to work.
Without reflection:
string typeName = "EventType"; // as param1 -> type name
string content = ""; //as param2 -> some json cotnent
IMyBus bus = new MyBus();
switch (typeName)
{
case "EventType":
IEventType content = Deserialize<IEventType>(content);
bus.Publish<IEventType>(content);
break;
case "EventType2":
IEventType2 content2 = Deserialize<IEventType2>(content);
bus.Publish<IEventType2>(content2);
break;
}
Instead I would like to rewrite it using reflection to supply more EventTypes without rewriting this particular code.
Below are my test classes:
public interface IMyBus **//TWO METHODS HERE, WE NEED TO CALL FIRST ONE!**
{
void Publish<T>(T message, CancellationToken cancellationToken = default(CancellationToken)) where T : class; //THIS SHOULD BE CALLED
void Publish<T>(T message, int i, CancellationToken cancellationToken = default(CancellationToken)) where T : class;
}
// Interface implementation
public class MyBus : IMyBus
{
public void Publish<T>(T message, CancellationToken cancellationToken = default(CancellationToken)) where T : class
{ ... }
public void Publish<T>(T message, int i, CancellationToken cancellationToken = default(CancellationToken)) where T : class
{ ... }
}
public interface IEventType { int forTest { get; set; } }
public class EventType : IEventType { public int forTest { get; set; } }
public interface IEventType2 { int forTest2 { get; set; } }
public class EventType2 : IEventType2 { public int forTest2 { get; set; } }
And here is what I already tried:
IMyBus bus = new MyBus();
EventType content = new EventType() { forTest = 1 };
var eventTypeName = $"ConsoleApp.EventType";
var iEventTypeName = $"ConsoleApp.IEventType";
Type intEvType = Type.GetType(iEventTypeName);
Type evType = Type.GetType(eventTypeName);
MethodInfo openGenericMethod = typeof(IMyBus).GetMethod("Publish", 2, new Type[] { intEvType, typeof(CancellationToken) });
MethodInfo closedGenericMethod = openGenericMethod.MakeGenericMethod(evType);
object o2 = closedGenericMethod.Invoke(bus, new object[] { content });
But this is wrong. openGenericMethod is always null.
I think for this situation better to find method manually based on name and number of parameters:
MethodInfo openGenericMethod = typeof(IMyBus).GetMethods()
.SingleOrDefault(m => m.Name == "Publish"
&& m.IsGenericMethod
&& m.GetParameters().Count() == 2);
But you are going to call method with optional parameters. That's why you should use other Invoke method where you can define binding flag:
MethodInfo closedGenericMethod = openGenericMethod.MakeGenericMethod(evType);
closedGenericMethod.Invoke(bus, BindingFlags.OptionalParamBinding, null,
new object[] { content, Type.Missing }, CultureInfo.InvariantCulture);
I've tested it in the test ConsoleApp. It works.

How to create a generic method to iterate through an object's fields and use it as a Where predicate?

I'm building a generic interface to expose selected string properties out of a class, and then I want to search for a text inside every one of those fields, to check if it's a match.
Here's my IFieldExposer interface:
using System;
using System.Collections.Generic;
public interface IFieldExposer<T>
{
IEnumerable<Func<T, string>> GetFields();
}
Now, I implement it like this in my DataClass to expose the properties I would like to iterate. Note that I'm also exposing a property from my ChildClass:
using System;
using System.Collections.Generic;
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> GetFields()
{
return new List<Func<DataClass, string>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
class ChildClass
{
public string PropertyThree { get; set; }
}
I've also created extension methods for IFieldExposer<T> because I want to keep it simple and be able to simply call obj.Match(text, ignoreCase) everywhere else in my code. This method should tell me if my object is a match for my text. Here's the code for the ExtensionClass, which isn't working as expected:
using System;
using System.Linq.Expressions;
using System.Reflection;
public static class ExtensionClass
{
public static bool Match<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
Func<bool> expression = Expression.Lambda<Func<bool>>(obj.CreateExpressionTree(text, ignoreCase)).Compile();
return expression();
}
private static Expression CreateExpressionTree<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
var exposedFields = obj.GetFields();
if (ignoreCase)
{
// How should I do convert these to lower too?
// exposedFields = exposedFields.Select(e => e.???.ToLower());
text = text.ToLower();
}
Expression textExp = Expression.Constant(text);
Expression orExpressions = Expression.Constant(false);
foreach (var field in exposedFields)
{
//How should I call the contains method on the string field?
Expression fieldExpression = Expression.Lambda<Func<string>>(Expression.Call(Expression.Constant(obj), field.Method)); //this doesn't work
Expression contains = Expression.Call(fieldExpression, containsMethod, textExp);
orExpressions = Expression.Or(orExpressions, contains);
}
return orExpressions;
}
}
Please check the comments in the code above. I would like to know how to convert all my string properties to lowercase (if desired) and how to call string.Contains in each one of them. I get this error when I create my fieldExpression:
Method 'System.String <GetFields>b__12_0(DataClass)' declared on type 'DataClass+<>c' cannot be called with instance of type 'DataClass'
I don't have experience working with Expression Trees. I've spent hours reading docs and other answers for similar issues but I still can't understand how to achieve what I want... I have no clue what to do now.
I'm testing this in a console app so here's the main class if you want to build it yourself:
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var data = new DataClass
{
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass
{
PropertyThree = "Dolor"
}
};
var dataList = new List<DataClass> { data };
var results = dataList.Where(d => d.Match("dolor", true));
}
}
EDIT
I forgot to mention that my dataList should be IQueryable and I want to execute my code in SQL, that's why I'm trying to build the expression trees myself. So it appears my example code should be:
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable();
var results = query.Where(ExtensionClass.Match<DataClass>("lorem dolor"));
while my method becomes: (I'm following #sjb-sjb's answer and changed the GetFields() method in IFieldExposer<T> to a SelectedFields property)
public static Expression<Func<T, bool>> Match<T>(string text, bool ignoreCase) where T : IFieldExposer<T>
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "obj");
MemberExpression selectedFieldsExp = Expression.Property(parameter, "SelectedFields");
LambdaExpression lambda = Expression.Lambda(selectedFieldsExp, parameter).Compile();
[...]
}
And then it seems that I have to dinamically call selectedFieldsExp with Expression.Lambda. I came up with:
Expression.Lambda(selectedFieldsExp, parameter).Compile();
and that works, but I don't know how to properly call DynamicInvoke() for the lambda expression.
It throws Parameter count mismatch. if I call it without parameters and Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'DataClass'. if I do DynamicInvoke(parameter).
Any ideas?
Before getting to the implementation, there are some design flaws that needs to be fixed.
First, almost all query providers (except LINQ to Object which simply compiles the lambda expressions to delegates and executes them) don't support invocation expressions and custom (unknown) methods. That's because they do not execute the expressions, but translate them to something else (SQL for instance), and translation is based on pre knowledge.
One example of invocation expression are Func<...> delegates. So the first thing you should do is to use Expression<Func<...>> wherever you currently have Func<...>.
Second, the query expression trees are built statically, i.e. there is no real object instance you can use to obtain the metadata, so the idea of IFieldExposer<T> won't work. You'd need a statically exposed list of expressions like this:
class DataClass //: IFieldExposer<DataClass>
{
// ...
public static IEnumerable<Expression<Func<DataClass, string>>> GetFields()
{
return new List<Expression<Func<DataClass, string>>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
Then the signature of the method in question could be like this
public static Expression<Func<T, bool>> Match<T>(
this IEnumerable<Expression<Func<T, string>>> fields, string text, bool ignoreCase)
with usage like this
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable()
.Where(DataClass.GetFields().Match("lorem", true));
Now the implementation. The desired expression could be built purely with Expression class methods, but I'll show you an easier (IMHO) method, which composes expression from compile time expression by replacing the parameter(s) with other expression(s).
All you need is a small helper utility method for replacing lambda expression parameter with another expression:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : base.VisitParameter(node);
}
}
Internally it uses ExpressionVistor to find each instance of the passed ParameterExpression and replace it with the passed Expression.
With this helper method, the implementation could be like this:
public static partial class ExpressionUtils
{
public static Expression<Func<T, bool>> Match<T>(this IEnumerable<Expression<Func<T, string>>> fields, string text, bool ignoreCase)
{
Expression<Func<string, bool>> match;
if (ignoreCase)
{
text = text.ToLower();
match = input => input.ToLower().Contains(text);
}
else
{
match = input => input.Contains(text);
}
// T source =>
var parameter = Expression.Parameter(typeof(T), "source");
Expression anyMatch = null;
foreach (var field in fields)
{
// a.PropertyOne --> source.PropertyOne
// b.Child.PropertyThree --> source.Child.PropertyThree
var fieldAccess = field.Body.ReplaceParameter(field.Parameters[0], parameter);
// input --> source.PropertyOne
// input --> source.Child.PropertyThree
var fieldMatch = match.Body.ReplaceParameter(match.Parameters[0], fieldAccess);
// matchA || matchB
anyMatch = anyMatch == null ? fieldMatch : Expression.OrElse(anyMatch, fieldMatch);
}
if (anyMatch == null) anyMatch = Expression.Constant(false);
return Expression.Lambda<Func<T, bool>>(anyMatch, parameter);
}
}
The input => input.ToLower().Contains(text) or input => input.Contains(text) is our compile time match expression, which we then replace the input parameter with the body of the passed Expression<Func<T, string>> lambda expressions, with their parameter replaced with a common parameter used in the final expression. The resulting bool expressions are combined with Expression.OrElse which is the equivalent of the C# || operator (while Expression.Or is for bitwise | operator and in general should not be used with logical operations). Same btw for && - use Expression.AndAlso and not Expression.And which is for bitwise &.
This process is pretty much the expression equivalent of the string.Replace. In case the explanations and code comments are not enough, you can step through the code and see the exact expression transformations and expression building process.
There is no need to get into the complexities of dynamically creating an Expression, because you can just invoke the Func delegate directly:
public interface IFieldExposer<T>
{
IEnumerable<Func<T,string>> SelectedFields { get; }
}
public static class FieldExposerExtensions
{
public static IEnumerable<Func<T,string>> MatchIgnoreCase<T>( this IEnumerable<Func<T,string>> stringProperties, T source, string matchText)
{
return stringProperties.Where(stringProperty => String.Equals( stringProperty( source), matchText, StringComparison.OrdinalIgnoreCase));
}
}
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> SelectedFields {
get {
return new Func<DataClass, string>[] { #this => #this.PropertyOne, #this => #this.Child.PropertyThree };
}
}
public override string ToString() => this.PropertyOne + " " + this.PropertyTwo + " " + this.Child.PropertyThree;
}
class ChildClass
{
public string PropertyThree { get; set; }
}
Then to use it,
class Program
{
static void Main(string[] args)
{
var data = new DataClass {
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass {
PropertyThree = "Dolor"
}
};
var data2 = new DataClass {
PropertyOne = "lorem",
PropertyTwo = "ipsum",
Child = new ChildClass {
PropertyThree = "doloreusement"
}
};
var dataList = new List<DataClass>() { data, data2 };
IEnumerable<DataClass> results = dataList.Where( d => d.SelectedFields.MatchIgnoreCase( d, "lorem").Any());
foreach (DataClass source in results) {
Console.WriteLine(source.ToString());
}
Console.ReadKey();
}
}
Following up on my comment above, I think you could do it like this:
class DataClass
{
…
static public Expression<Func<DataClass,bool>> MatchSelectedFields( string text, bool ignoreCase)
{
return #this => (
String.Equals( text, #this.PropertyOne, (ignoreCase? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal))
|| String.Equals( text, #this.Child.PropertyThree, (ignoreCase? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal))
);
}
}
Then the query is just
Expression<Func<DataClass,bool>> match = DataClass.MatchSelectedFields( "lorem", ignoreCase);
IEnumerable<DataClass> results = dataList.Where( d => match(d));
I wouldn't usually post a second answer but I thought it would be useful to see how to avoid dynamic modification of Expressions.
Caveat: I didn't actually try to compile it.

Invoke methodinfo inherited from closed generic

private static void Main(string[] args)
{
var messageType = typeof (SampleHandler1);
var genericType = typeof (IConsume<>).MakeGenericType(messageType);
var genericArguments = genericType.GetGenericArguments();
var consumeMethod = genericType.GetMethod("Consume");
var constructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
var classObject = constructorInfo.Invoke(new object[] {});
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
consumeMethod.Invoke(classObject, argsx);
}
public interface IConsume<in T> where T : class
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Debugger.Break();
}
public void Consume(SampleMessage message)
{
Debugger.Break();
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
I tried looking here but I cant find specific solution. As MSDN explains
obj
Type: System.Object
The object on which to invoke the method or constructor. If a method is static, this argument is ignored. If a constructor is static, this argument must be null or an instance of the class that defines the constructor.
classObject is an instance of constructor, right? Why it throws an exception:
That doesn't seem right. Let's analyze what's happening here:
var messageType = typeof (SampleHandler1);
//simple enough, Type -> SampleHandler1
var genericType = typeof (IConsume<>).MakeGenericType(messageType);
//so genericType is a Type -> IConsume<SampleHandler1>
var genericArguments = genericType.GetGenericArguments();
//there's only one, but Type[] { Type -> SampleHandler1 }
var consumeMethod = genericType.GetMethod("Consume");
//MethodInfo -> IConsume<SampleHandler1>.Consume(SampleHandler1)
var constructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
//ConstructorInfo -> SampleHandler1..ctor()
var classObject = constructorInfo.Invoke(new object[] {});
//new SampleHandler1()
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
//object[] { SampleMessage }
consumeMethod.Invoke(classObject, argsx);
//((IConsume<SampleHandler1>)classObject).Consume( SampleMessage ) -- oops?
So classObject is an SampleHandler1, but you're trying to invoke IConsume<SampleHandler1>.Consume(SampleHandler1) and worse yet give it a SampleMessage as an argument.
I think you meant to create a SampleHandler1, and invoke IConsume<SampleMessage>.Consume(SampleMessage) on it:
var messageType = typeof(SampleMessage);
var genericType = typeof(IConsume<>).MakeGenericType(messageType);
var consumeMethod = genericType.GetMethod("Consume");
var handlerType = typeof(SampleHandler1);
var constructorInfo = handlerType.GetConstructor(Type.EmptyTypes);
var classObject = constructorInfo.Invoke(new object[] {});
var argsx = new object[] {new SampleMessage {Name = "sample message"}};
consumeMethod.Invoke(classObject, argsx);
I'm not sure, but based on all of the components you have in your question, I suspect that you are looking for something more like this:
using System;
public class Program
{
public static void Main()
{
var handlerType = typeof (SampleHandler1);
var genericType = handlerType.GetInterface("IConsume`1");
var genericArguments = genericType.GetGenericArguments();
var consumeMethod = genericType.GetMethod("Consume");
var handlerConstructorInfo = handlerType.GetConstructor(Type.EmptyTypes);
var handler = handlerConstructorInfo.Invoke(new object[] {});
var messageConstructorInfo = genericArguments[0].GetConstructor(Type.EmptyTypes);
var messageObject = messageConstructorInfo.Invoke(new object[] {});
((IBaseMessage)messageObject).Name = "Sample Message";
var argsx = new object[] {messageObject};
consumeMethod.Invoke(handler, argsx);
}
}
public interface IConsume<in T> where T : class, IBaseMessage
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Console.WriteLine("SampleHandler1 constructed");
}
public void Consume(SampleMessage message)
{
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
string Name { get; set; }
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
Here is a working Dotnetfiddle of the above answer: https://dotnetfiddle.net/YFmmzk
The console output is:
SampleHandler1 constructed
Message consume: Sample Message
It looks like you were getting your handler and message types confused. You were trying to pass an instance of the handler itself to the consume method. More over, IBaseMessage was missing the Name property declaration.
UPDATE
Here is a cleaned up version of this answer:
public class Program
{
public static void Main()
{
var handler = new DynamicConstructor(typeof (SampleHandler1)).New();
invokeIConsumeFor(handler, "Sample Message");
}
private static void invokeIConsumeFor(object handler, string message)
{
var executer = new DynamicGenericInterfaceExecuter(handler, "IConsume`1");
var messageObject = executer.GetTypeArgumentConstructor(0, Type.EmptyTypes).New();
((IBaseMessage) messageObject).Name = message;
executer.Method("Consume", messageObject.GetType()).Call(messageObject);
}
}
public class DynamicGenericInterfaceExecuter
{
private object instance;
private Type genericInterfaceFromType;
private Type[] genericTypeArguments;
public DynamicGenericInterfaceExecuter(object instance, string interfaceName)
{
this.instance = instance;
this.genericInterfaceFromType = instance.GetType().GetInterface(interfaceName);
this.genericTypeArguments = this.genericInterfaceFromType.GetGenericArguments();
}
public MethodExecuter Method(string methodName, params Type[] parameterTypes)
{
return new MethodExecuter(this.instance, this.genericInterfaceFromType, methodName, parameterTypes);
}
public DynamicConstructor GetTypeArgumentConstructor(int typeArgumentIndex, params Type[] constructorParameterTypes)
{
return new DynamicConstructor(this.genericTypeArguments[typeArgumentIndex], constructorParameterTypes);
}
}
public class DynamicConstructor
{
private System.Reflection.ConstructorInfo constructor;
public DynamicConstructor(Type type, params Type[] constructorParameters)
{
this.constructor = type.GetConstructor(constructorParameters);
}
public object New(params object[] constructorArguments)
{
return this.constructor.Invoke(constructorArguments);
}
}
public class MethodExecuter
{
private object instance;
private System.Reflection.MethodInfo method;
public MethodExecuter(object instance, Type containerType, string methodName, Type[] methodParameters)
{
this.instance = instance;
this.method = containerType.GetMethod(methodName, methodParameters);
}
public void Call(params object[] arguments)
{
this.Invoke(arguments);
}
public object Invoke(params object[] arguments)
{
return this.method.Invoke(instance, arguments);
}
}
public interface IConsume<in T> where T : class, IBaseMessage
{
void Consume(T message);
}
public class SampleHandler1 : IConsume<SampleMessage>
{
public SampleHandler1()
{
Console.WriteLine("SampleHandler1 constructed");
}
public void Consume(SampleMessage message)
{
Console.WriteLine("Message consume: " + message.Name);
}
}
public interface IBaseMessage
{
string Name { get; set; }
}
public class SampleMessage : IBaseMessage
{
public string Name { get; set; }
}
And the dotnetfiddle: https://dotnetfiddle.net/n9WHZ2
Keep in mind that this is not type safe in the slightest, but that does not appear to be a concern in your question.

Call Static Method in expression.call with arguments

I have extended the string class for Contains method. I'm trying to call it in Expression.Call, but how to pass the argument properly?
Code: String Contains method:
public static class StringExts
{
public static bool NewContains(this string source, string ValToCheck, StringComparison StrComp)
{
return source.IndexOf(ValToCheck, StrComp) >= 0;
}
}
In Expression calling as :
public class Person { public string Name {get; set;} }
public class Persons {
public List<Person> lstPersons {get; set;}
public Persons() {
lstPersons = new List<Person>();
}
}
public class Filter
{
public string Id { get; set; }
public Operator Operator { get; set; }
public string value { get; set; }
}
public void Main()
{
//Get the json.
//"Filters": [{"id": "Name", "operator": "contains", "value": "Microsoft"}]
Filter Rules = JsonConvert.DeserializeObject<Filter>(json);
// Get the list of person firstname.
List<Person> lstPerson = GetFirstName();
ParameterExpression param = Expression.Parameter(typeof(Person), "p");
Expression exp = null;
exp = GetExpression(param, rules[0]);
//get all the name contains "john" or "John"
var filteredCollection = lstPerson.Where(exp).ToList();
}
private Expression GetExpression(ParameterExpression param, Filter filter){
MemberExpression member = Expression.Property(param, filter.Id);
ConstantExpression constant = Expression.Constant(filter.value);
Expression bEXP = null;
switch (filter.Operator)
{
case Operator.contains:
MethodInfo miContain = typeof(StringExts).GetMethod("NewContains", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
return Expression.Call(miContain, member, constant , Expression.Constant(StringComparison.OrdinalIgnoreCase));;
break;
}
}
Error:
An unhandled exception of type 'System.ArgumentException' occurred in System.Core.dll.Additional information: Static method requires null instance, non-static method requires non-null instance.
How to call the parameter in miContain for following Call() methods?
I have updated the Code.
You are not specifying all parameters. If you create expressions for all, it works:
ParameterExpression source = Expression.Parameter(typeof(string));
string ValToCheck = "A";
StringComparison StrComp = StringComparison.CurrentCultureIgnoreCase;
MethodInfo miContain = typeof(StringExts).GetMethod("NewContains", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
var bEXP = Expression.Call(miContain, source, Expression.Constant(ValToCheck), Expression.Constant(StrComp));
var lambda = Expression.Lambda<Func<string, bool>>(bEXP, source);
bool b = lambda.Compile().Invoke("a");
You haven't specified enough arguments (2 vs. 3). NewContains has three arguments.
Also, since this method is not an instance method you can't set the this parameter. This overload looks better.
You probably should have examined the overload list. That is how I found the right answer to this question without knowing it myself beforehand.

Categories

Resources