I am trying to write code that receives an argument list and gets the ConstructorInfo of a matching Ctor.
The method signature is ConstructorInfo GetConstructorInfo(Type type, object[] args).
I have created a class to work with:
public class ClassWithParamsInCtor
{
public ClassWithParamsInCtor(params int[] parameters)
{
}
}
Using the Activator class I can create instances of this object:
ClassWithParamsInCtor myclass = Activator.CreateInstance(typeof(ClassWithParamsInCtor), new object[] { 1,2 }) as ClassWithParamsInCtor; \\returns a valid instance of the class;
But when I try to get the ConstructorInfo there is an issue, the following returns null:
ConstructorInfo ctorInfo = typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int), typeof(int) }); \\returns null
How can I obtain the ConstructorInfo in such a case ?
params int[] is a syntactic sugar for int[], you need to use typeof(int[])
typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int[]) });
Try this,
ConstructorInfo ctorInfo = typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int[])});
Hope helps,
I think that I have a solution for your problem if I understood it right.
Assuming you have the following class :
public class ClassWithParamsInCtor
{
public ClassWithParamsInCtor(params int[] parameters)
{
}
public ClassWithParamsInCtor(int[] parameters, double y)
{
}
public ClassWithParamsInCtor(int[] parameters, float y)
{
}
}
Here are some Unit Tests that describe the solution that I've implemented :
[TestClass]
public class TestClassWithParamsInCtorClass
{
[TestMethod]
//Wrong data type test
public void WrongInputDataTypesTest()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 }, "" };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
//Assert
Assert.AreEqual(null, matchedCtorParams.FirstOrDefault());
}
[TestMethod]
//Test used to invoke first constructor
public void InputDataTypesTest1()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 } , 1 };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
.Invoke(inputData);
//Assert
Assert.AreNotEqual(null, result);
}
[TestMethod]
//Test used to invoke second constructor
public void InputDataTypesTest2()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 }, 1.2 };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
.Invoke(inputData);
//Assert
Assert.AreNotEqual(null, result);
}
[TestMethod]
//Test used to invoke third constructor
public void InputDataTypesTest3()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 }, 3.5F };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
.Invoke(inputData);
//Assert
Assert.AreNotEqual(null, result);
}
}
And the extension method that I used :
public static bool HaveSameItems<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
var dictionary = a.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
foreach (var item in b)
{
int value;
if (!dictionary.TryGetValue(item, out value))
{
return false;
}
if (value == 0)
{
return false;
}
dictionary[item] -= 1;
}
return dictionary.All(x => x.Value == 0);
}
I'm pretty sure that the Unit tests can be implemented to be more granular specific, but considering this as a proofe of concept, I find them decent.
Related
Oversimplified example:
[TestMethod]
public void ReactiveCatch() {
var source = new[] { new { i = 1 }, new { i = 0 } }.ToObservable();
var a = source
.Do(x => { var j = 1 / x.i; })
.Catch((Exception exc) => Observable.Empty(new { i = 0 }))
.ToEnumerable()
.ToArray();
Assert.AreEqual(new { i = 1 }, a[0]);
}
Is there a way to provide a generic handler to the Catch method, which will return an empty default IObservable?
Came up with this solution after carefully reading the comment.
static class Mixin {
public static IObservable<T> CatchAndStop<T>(this IObservable<T> source)
=> source.Catch((Exception exc) => Observable.Empty<T>());
}
[TestClass]
public class ReactiveExtensions {
[TestMethod]
public void ReactiveCatch() {
var source = new[] { new { i = 1 }, new { i = 0 } }.ToObservable();
var a = source
.Do(x => { var j = 1 / x.i; })
.CatchAndStop()
.ToEnumerable()
.ToArray();
Assert.AreEqual(new { i = 1 }, a[0]);
}
But what if I need to Catch an exception of specific type? Something like .CatchAndStop<TimeoutException>().
IObservable<TSource> Catch<TSource, TException>(...) has two generic parameters. Both of them need to be inferred in order to not specify them.
A dummy Func<T> to the rescue.
static class Mixin {
public static IObservable<TSource> CatchAndStop<TSource>(
this IObservable<TSource> source) => source.CatchAndStop(() => new Exception());
public static IObservable<TSource> CatchAndStop<TSource, TException>(
this IObservable<TSource> source, Func<TException> witness
) where TException : Exception
=> source.Catch((TException exc) => Observable.Empty<TSource>());
}
[TestClass]
public class ReactiveExtensions {
[TestMethod]
public void ReactiveCatch() {
var source = new[] { new { i = 1 }, new { i = 0 } }.ToObservable();
var a = source
.Do(x => { var j = 1 / x.i; })
.CatchAndStop(() => new TimeoutException())
.CatchAndStop()
.ToEnumerable()
.ToArray();
Assert.AreEqual(new { i = 1 }, a[0]);
}
TSource is inferred from the IObservable<TSource> source
TException will be inferred from the second parameter - witness, which serves purely as a holder for TException's generic parameter type.
Not ideal, but whatever works ...
I'm pretty new to xUnit and here's what I'd like to achieve:
[Theory]
[InlineData((Config y) => y.Param1)]
[InlineData((Config y) => y.Param2)]
public void HasConfiguration(Func<Config, string> item)
{
var configuration = serviceProvider.GetService<GenericConfig>();
var x = item(configuration.Config1); // Config1 is of type Config
Assert.True(!string.IsNullOrEmpty(x));
}
Basically, I have a GenericConfig object which contains Config and other kind of configurations, but I need to check that every single parameter is valid. Since they're all string, I wanted to simplify using [InlineData] attribute instead of writing N equals tests.
Unfortunately the error I'm getting is "Cannot convert lambda expression to type 'object[]' because it's not a delegate type", which is pretty much clear.
Do you have any idea on how to overcome this?
In addition to the already posted answers. The test cases can be simplified by directly yielding the lambdas.
public class ConfigTestDataProvider
{
public static IEnumerable<object[]> TestCases
{
get
{
yield return new object [] { (Func<Config, object>)((x) => x.Param1) };
yield return new object [] { (Func<Config, object>)((x) => x.Param2) };
}
}
}
This test ConfigTestDataProvider can then directly inject the lambdas.
[Theory]
[MemberData(nameof(ConfigTestCase.TestCases), MemberType = typeof(ConfigTestCase))]
public void Test(Func<Config, object> func)
{
var config = serviceProvider.GetService<GenericConfig>();
var result = func(config.Config1);
Assert.True(!string.IsNullOrEmpty(result));
}
Actually, I was able to find a solution which is a bit better than the one provided by Iqon (thank you!).
Apparently, the InlineData attribute only supports primitive data types. If you need more complex types, you can use the MemberData attribute to feed the unit test with data from a custom data provider.
Here's how I solved the problem:
public class ConfigTestCase
{
public static readonly IReadOnlyDictionary<string, Func<Config, string>> testCases = new Dictionary<string, Func<Config, string>>
{
{ nameof(Config.Param1), (Config x) => x.Param1 },
{ nameof(Config.Param2), (Config x) => x.Param2 }
}
.ToImmutableDictionary();
public static IEnumerable<object[]> TestCases
{
get
{
var items = new List<object[]>();
foreach (var item in testCases)
items.Add(new object[] { item.Key });
return items;
}
}
}
And here's the test method:
[Theory]
[MemberData(nameof(ConfigTestCase.TestCases), MemberType = typeof(ConfigTestCase))]
public void Test(string currentField)
{
var func = ConfigTestCase.testCases.FirstOrDefault(x => x.Key == currentField).Value;
var config = serviceProvider.GetService<GenericConfig>();
var result = func(config.Config1);
Assert.True(!string.IsNullOrEmpty(result));
}
I could maybe come up with something a bit better or cleaner, but for now it works and the code is not duplicated.
I have the problem the same to you, and I found the solution that using TheoryData class and MemberData attribute. Here is the example and I hope the code usefully:
public class FooServiceTest
{
private IFooService _fooService;
private Mock<IFooRepository> _fooRepository;
//dummy data expression
//first parameter is expression
//second parameter is expected
public static TheoryData<Expression<Func<Foo, bool>>, object> dataExpression = new TheoryData<Expression<Func<Foo, bool>>, object>()
{
{ (p) => p.FooName == "Helios", "Helios" },
{ (p) => p.FooDescription == "Helios" && p.FooId == 1, "Helios" },
{ (p) => p.FooId == 2, "Poseidon" },
};
//dummy data source
public static List<Foo> DataTest = new List<Foo>
{
new Foo() { FooId = 1, FooName = "Helios", FooDescription = "Helios Description" },
new Foo() { FooId = 2, FooName = "Poseidon", FooDescription = "Poseidon Description" },
};
//constructor
public FooServiceTest()
{
this._fooRepository = new Mock<IFooRepository>();
this._fooService = new FooService(this._fooRepository.Object);
}
[Theory]
[MemberData(nameof(dataExpression))]
public void Find_Test(Expression<Func<Foo, bool>> expression, object expected)
{
this._fooRepository.Setup(setup => setup.FindAsync(It.IsAny<Expression<Func<Foo, bool>>>()))
.ReturnsAsync(DataTest.Where(expression.Compile()));
var actual = this._fooService.FindAsync(expression).Result;
Assert.Equal(expected, actual.FooName);
}
}
Oddly delegates are not objects, but Actions or Funcs are. To do this, you have to cast the lambda to one of these types.
object o = (Func<Config, string>)((Config y) => y.Param1)
But doing this, your expression is not constant anymore. So this will prevent usage in an Attribute.
There is no way of passing lambdas as attributes.
One possible solution would be to use function calls, instead of attributes. Not as pretty, but could solve your problem without duplicate code:
private void HasConfiguration(Func<Config, string> item)
{
var configuration = serviceProvider.GetService<GenericConfig>();
var x = item(configuration.Config1); // Config1 is of type Config
Assert.True(!string.IsNullOrEmpty(x));
}
[Theory]
public Test1()
{
HasConfiguration((Config y) => y.Param1);
}
[Theory]
public Test2()
{
HasConfiguration((Config y) => y.Param2);
}
public class HrcpDbTests
{
[Theory]
[MemberData(nameof(TestData))]
public void Test(Expression<Func<bool>> exp)
{
// Arrange
// Act
// Assert
}
public static IEnumerable<object[]> TestData
{
get
{
Expression<Func<bool>> mockExp1 = () => 1 == 0;
Expression<Func<bool>> mockExp2 = () => 1 != 2;
return new List<object[]>
{
new object[]
{
mockExp1
},
new object[]
{
mockExp2
}
}
}
}
}
I'm working with some legacy code which I require to write some unit tests for. There is data access method with the following signature.
Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters,
params Action<System.Data.IDataReader>[] actions);
which there is an implementation in the class i'm testing similar to this
private async Task<CustomObject> GetCustomObject(int id)
{
CustomObject obj = null;
await db.ExecuteReaderAsync("nameOfProcedure", some parameters,
dr =>
{
obj = new CustomObject()
{
Prop1 = dr["Col1"],
Prop2 = dr["Col2"]
}
}
return obj;
}
What I'm struggling with is being able to control the values returned by GetCustomObject. If ExecuteReaderAsync actually returned something I could have a set up like this.
mockDataAccess.Setup(x => x.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>()))
.Returns(Task.FromResult(new CustomeObject() { prop1 = "abc", prop2 = "def"};));
But the logic for specifying the values is an Action<IDataReader> which I don't control. I'm wondering if there are any tricks I could employ to do what I want,
i.e. control the value of the object returned by GetCustomObject.
Take a look at the following example
[TestClass]
public class LegacyCodeTest {
[TestMethod]
public async Task TestExecuteReaderAsync() {
//Arrange
var mapping = new Dictionary<string, string> {
{ "Col1", "abc" },
{ "Col2", "def" }
};
var mockDataReader = new Mock<IDataReader>();
mockDataReader
.Setup(m => m[It.IsAny<string>()])
.Returns<string>(col => mapping[col])
.Verifiable();
var mockDataAccess = new Mock<IDataAccess>();
mockDataAccess
.Setup(m => m.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>(), It.IsAny<Action<System.Data.IDataReader>[]>()))
.Returns(Task.FromResult<object>(null))
.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => {
if (a != null && a.Length > 0) {
a.ToList().ForEach(callback => callback(mockDataReader.Object));
}
})
.Verifiable();
var sut = new SUT(mockDataAccess.Object);
//Act
var actual = await sut.MUT(2);
//Assert
mockDataAccess.Verify();
mockDataReader.Verify(m => m["Col1"]);
mockDataReader.Verify(m => m["Col2"]);
actual.Should()
.NotBeNull()
.And
.Match<CustomObject>(c => c.Prop1 == mapping["Col1"] && c.Prop2 == mapping["Col2"]);
}
public interface IDataAccess {
Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters, params Action<System.Data.IDataReader>[] actions);
}
public class Parameters { }
public class CustomObject {
public object Prop1 { get; set; }
public object Prop2 { get; set; }
}
public class SUT {
IDataAccess db;
public SUT(IDataAccess dataAccess) {
this.db = dataAccess;
}
public async Task<CustomObject> MUT(int id) {
var result = await GetCustomObject(id);
return result;
}
private async Task<CustomObject> GetCustomObject(int id) {
CustomObject obj = null;
await db.ExecuteReaderAsync("nameOfProcedure", null,
dr => {
obj = new CustomObject() {
Prop1 = dr["Col1"],
Prop2 = dr["Col2"]
};
});
return obj;
}
}
}
Since you don't have control of the Action<IDataReader>, in this case the most that can be done is to make sure that the action does not fail. So that would mean passing an a mock reader that performs as expected for the action.
var mapping = new Dictionary<string, string> {
{ "Col1", "abc" },
{ "Col2", "def" }
};
var mockDataReader = new Mock<IDataReader>();
mockDataReader
.Setup(m => m[It.IsAny<string>()])
.Returns<string>(col => mapping[col])
.Verifiable();
by using the call back to get access to the passed in parameters
.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => {
if (a != null && a.Length > 0) {
a.ToList().ForEach(callback => callback(mockDataReader.Object));
}
})
the mocked reader can be passed to the actions called within the method under test.
This answer is tailored to the provided example in th OP so some modification may need to be made for it to apply to your specific scenario. This should be enough to get you going for situations like this.
I'm trying to mock this method
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc)
like this
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>())).ReturnsAsync(new { isPair = false });
The method to test doing the call passing an anonymous type to the generic parameter like this
instance.GetResultAsync(u => new {isPair = u == "something" }) //dont look at the function return because as generic could have diferent implementations in many case
Moq never matches my GetResultAsync method with the parameters sent.
I'm using Moq 4
The anonymous type is going to cause you problems. You need a concrete type for this to work.
The following example worked when I changed
instance.GetResultAsync(u => new {isPair = u == "something" })
to
instance.GetResultAsync(u => (object) new {isPair = u == "something" })
Moq is unable to match the anonymous type and that is why you get null when called.
[TestClass]
public class MoqUnitTest {
[TestMethod]
public async Task Moq_Function_With_Anonymous_Type() {
//Arrange
var expected = new { isPair = false };
var iMock = new Mock<IService>();
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>()))
.ReturnsAsync(expected);
var consumer = new Consumer(iMock.Object);
//Act
var actual = await consumer.Act();
//Assert
Assert.AreEqual(expected, actual);
}
public interface IService {
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
}
public class Consumer {
private IService instance;
public Consumer(IService service) {
this.instance = service;
}
public async Task<object> Act() {
var result = await instance.GetResultAsync(u => (object)new { isPair = u == "something" });
return result;
}
}
}
if the code calling the GetResultAsync is dependent on using the anonymous type then what you are trying to do with your test wont work with your current setup. You would probably need to provide a concrete type to the method.
[TestClass]
public class MoqUnitTest {
[TestMethod]
public async Task Moq_Function_With_Concrete_Type() {
//Arrange
var expected = new ConcreteType { isPair = false };
var iMock = new Mock<IService>();
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, ConcreteType>>()))
.ReturnsAsync(expected);
var sut = new SystemUnderTest(iMock.Object);
//Act
var actual = await sut.MethodUnderTest();
//Assert
Assert.AreEqual(expected, actual);
}
class ConcreteType {
public bool isPair { get; set; }
}
public interface IService {
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
}
public class SystemUnderTest {
private IService instance;
public SystemUnderTest(IService service) {
this.instance = service;
}
public async Task<object> MethodUnderTest() {
var result = await instance.GetResultAsync(u => new ConcreteType { isPair = u == "something" });
return result;
}
}
}
Edit2: removed a load of gubbins + bounty.
I have deconstructed an expression for a message bus in the hope of reconstructing it and invoking it in a slightly different manner. The serialization and deserialization is successful and I am able to create instances of most of what I need.
//Deconstruct
Expression<Func<T, Task>> expression
proxy => proxy.serviceMethod(arg);
I need to create the syntax below. T is an interface to a WCF service. This expression will be passed to a service invoker where it's internal ChannelFactory will pass it into this method.
//Reconstruct this as expression so I can pass it as a parameter
var myAction = new Action<T>(proxy => {
proxy.serviceMethod((SomeType)SomeParameter));
});
// to pass to this method
serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { myAction });
What I have:
//I think i'm nearly there I can create the inner call and assign
//the correct parameter, but I can't seem to figure out how to wrap it in an
// new Action<serviceT> { my other expressions... }
// Types
var serviceT = Type.GetType(workOutMessage.interfaceType);
var actionT = typeof(Action<>).MakeGenericType(serviceT);
var envelopeT = Type.GetType(workOutMessage.methodArgTypes[0]);
// ServiceInvoker<T> Instantiation - Works
var serviceInvokerT = typeof(HubServiceInvoker<>).MakeGenericType(serviceT);
var serviceInvokerTMethod = serviceInvokerT.GetMethod("InvokeService");
var serviceInvokerTInstance = Activator.CreateInstance(serviceInvokerT, client.Id, "password", clientCert, serviceCert);
// Expression Type Params
var serviceTParam = Expression.Parameter(serviceT, "proxy");
var envelopeParam = Expression.Parameter(envelopeT, "envelope");
var envAssign = Expression.Assign(envelopeParam, Expression.Constant(workOutMessage.methodArgs[0]));
var methodCall = Expression.Call(serviceTParam, serviceT.GetMethod(workOutMessage.methodName), envelopeParam);
// var lambda = ...
// make new Action<serviceT> myAction = { proxy => proxy.someMethod(someParameter); };
serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { lambda.Compile() });
Edit: The Service Invoker Method I pass this into to, to try and give the problem better context.
public void InvokeService(Action<T> handler)
{
T proxy = channelFactory.CreateChannel();
((IClientChannel)proxy).Faulted += ChannelFaulted;
ICommunicationObject obj2 = (ICommunicationObject)proxy;
try
{
using (new OperationContextScope((IContextChannel)proxy))
{
handler.Invoke(proxy);
}
}
finally
{
try
{
if (obj2.State != CommunicationState.Faulted)
{
obj2.Close();
}
}
catch
{
obj2.Abort();
}
}
}
Here is a full piece of code, in which I assume you only need a Func, not an Action.
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace HelloWorld
{
public class Service1
{
public Task ServiceMethod(string something)
{
return Task.Factory.StartNew(() => Console.WriteLine(something));
}
}
public class HubServiceInvoker<T> where T : new()
{
T t;
public HubServiceInvoker(string id, string password)
{
t = new T();
}
public void InvokeService(Func<T, Task> serviceInvoker)
{
Task task = serviceInvoker(t);
}
public static Func<T, Task> CompileInvoker(Expression expression, ParameterExpression serviceTParam)
{
Expression<Func<T, Task>> lambda = Expression.Lambda<Func<T, Task>>(expression, serviceTParam);
return lambda.Compile();
}
}
public class WorkOutMessage
{
public string interfaceType { get; set; }
public string[] methodArgTypes { get; set; }
public object[] methodArgs { get; set; }
public string methodName { get; set; }
}
static class Program
{
static void Main(string[] args)
{
WorkOutMessage workOutMessage = new WorkOutMessage()
{
interfaceType = "HelloWorld.Service1",
methodArgTypes = new string[] { "System.String" },
methodArgs = new object[] { "yeah it works!" },
methodName = "ServiceMethod"
};
InvokeService(workOutMessage);
Console.Read();
}
static void InvokeService(WorkOutMessage workOutMessage)
{
// Types
var serviceT = Type.GetType(workOutMessage.interfaceType);
// ServiceInvoker<T> Instantiation - Works
var serviceInvokerT = typeof(HubServiceInvoker<>).MakeGenericType(serviceT);
var serviceInvokerTMethod = serviceInvokerT.GetMethod("InvokeService");
var serviceCompileInvokerTMethod = serviceInvokerT.GetMethod("CompileInvoker");
var serviceInvokerTInstance = Activator.CreateInstance(serviceInvokerT, "id", "password");
// Expression Type Params
var serviceTParam = Expression.Parameter(serviceT, "proxy");
var methodCall = Expression.Call(serviceTParam, serviceT.GetMethod(workOutMessage.methodName), Expression.Constant(workOutMessage.methodArgs[0]));
serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] {
serviceCompileInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { methodCall, serviceTParam })
});
}
}
}
It was a little bit hard to understand what exactly is going on.
Perhaps, this will help you a little bit further:
// Helper.cs
public static Action<TType> Wrap<TType>(Delegate test)
{
return ret => test.DynamicInvoke();
}
var meth = typeof(Helper).GetMethod("Wrap");
var gmeth = meth.MakeGenericMethod(new[] { serviceT });
var genericAction = gmeth.Invoke(null, new object[] {
Expression.Lambda(methodCall).Compile(); });
Thanks to the hints from #Romain Hautefeuille, the key was to use my generic ServiceInvoker class to help me create the action that I needed without the need of using Expressions (woohoo).
// Execute Interface method from interface, methodName and methodArgs from message queue
var serviceT = Type.GetType(workOutMessage.interfaceType);
var serviceInvokerT = typeof(HubServiceInvoker<>).MakeGenericType(serviceT);
var serviceInvokerTMethod = serviceInvokerT.GetMethod("InvokeService");
var serviceInvokerCompileTMethod = serviceInvokerT.GetMethod("CompileServiceMethod");
var serviceInvokerTInstance = Activator.CreateInstance(serviceInvokerT, client.Id,
"password", clientCert, serviceCert);
// Works! and a lot simpler
serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] {
workOutMessage.correlationId,
serviceInvokerCompileTMethod.Invoke(serviceInvokerTInstance, new object[] {
workOutMessage.methodName,
workOutMessage.methodArgs })
});
And Finally the new method in the ServiceInvoker class (I wanted to avoid reflection in this class - for no particular reason - but it doesn't affect calling it normally).
public Action<T> CompileServiceMethod(string methodName, object[] methodArguments)
{
return new Action<T>(proxy =>
{
typeof(T).GetMethod(methodName).Invoke(proxy, methodArguments);
});
}