Set constructor parameter type for structuremap Registry - c#

This is the follow up question to thread: How to pass parent/child class as constructor parameter with structuremap?
Thanks PHeiberg. Really helps. Got another two questions:
Why do we need PricingHandlerFactory constructor there?
What it frustrate me is that actually PricingHandler is not invoked directly, we are using reflection and Registry to find PricingHandler. i.e.(Not all codes are pasted.)
public class MessageHandlerRegistry : Registry
{
public MessageHandlerRegistry()
{
Scan(x =>
{
x.AssemblyContainingType(typeof(PricingHandler));
x.ConnectImplementationsToTypesClosing(typeof(IMessageHandler<>));
});
}
}
private object QueryHandlerExecute(IMessage message)
{
var handlerType = typeof(IMessageHandler<>);
var genericType = handlerType.MakeGenericType(message.GetType());
var messageHandler = ObjectFactory.GetInstance(genericType);
return messageHandler.GetType().GetMethod("Execute")
.Invoke(messageHandler, new object[] { message });
}
The actual declaration of PricingHandler is:
public class PricingHandler : IMessageHandler<PricingMessage>
{
public PricingHandler(IPricingFactorsRepository pricingFactorsRepository)
public object Execute(PricingMessage pricingMsg){}
}
Simply speaking, we find PricingHandler by IMessageHandler<PricingMessage>. So we can't use
ObjectFactory.GetInstance<PricingHandler>();
to find PricingHandler and set the type of constructor parameter.
So, in such case, how can we set the constructor parameter type?
Edit1.
For now, I am updating the 'FACTORY' class to allow specifying named instance(pricinghandler.)
private object QueryHandlerExecute(IMessage message,string instanceName)
{
var handlerType = typeof(IMessageHandler<>);
var genericType = handlerType.MakeGenericType(message.GetType());
var messageHandler = string.IsNullOrEmpty(instanceName)
? ObjectFactory.GetInstance(genericType)
: ObjectFactory.GetNamedInstance(genericType, instanceName);
return messageHandler.GetType().GetMethod("Execute").Invoke(messageHandler, new object[] { message });
}

Related

Is there a way to write a method so that Method<A>(C obj) compiles? [duplicate]

I have a generic method like this (simplified version):
public static TResult PartialInference<T, TResult>(Func<T, TResult> action, object param)
{
return action((T)param);
}
In the above, param is of type object on purpose. This is part of the requirement.
When I fill in the types, I can call it like this:
var test1 = PartialInference<string, bool>(
p => p.EndsWith("!"), "Hello world!"
);
However, I'd like to use type inference. Preferably, I would like to write this:
var test2 = PartialInference<string>(
p => p.EndsWith("!"), "Hello world!"
);
But this does not compile. The best I came up with is this:
var test3 = PartialInference(
(string p) => p.EndsWith("!"), "Hello world!"
);
The reason I would like to have this as a type parameter and still have the correctly typed return type is because my actual calls look something like this:
var list1 = ComponentProvider.Perform(
(ITruckSchedule_StaffRepository p) => p.GetAllForTruckSchedule(this)
)
Which is very ugly and I would love to write as something like this:
var list2 = ComponentProvider.Perform<ITruckSchedule_StaffRepository>(
p => p.GetAllForTruckSchedule(this)
)
You can split t into a generic method on a generic type:
class Foo<TOuter> {
public static void Bar<TInner>(TInner arg) {...}
}
...
int x = 1;
Foo<string>.Bar(x);
Here the int is inferred but the string is explicit.
What you are trying to achieve is not possible. You need to specify both generic arguments or none of the them if inference is possible.
You can use reflection... like this below
Here is an example of how to call a extension method with two generic parameters.
We have to ways to execute the extension method:
a) Directly from an abstract base class
b) From an instance object that derived from that base class
Not mandatory to implement like so, but I found it very handy.
a) You must supply the two generic arguments as usual.
b) You already have one of the generic types since you are using an instance. The other generic parameter must by passed as type argument, you cannot pass it a second generic parameter due to ambiguity.
(see How to pass 2 generics types into an extension method)
public interface IEntityDto
{
// Not relevant to this example, how is defined , is just an interface, it could be removed, if your generic types don't need interface constraints
}
public interface IRowVersion
{
// Not relevant to this example, how is defined , is just an interface, it could be removed, if your generic types don't need interface constraints
}
public interface IPropertyMappingValue
{
// Not relevant to this example, how is defined , is just an interface, it could be removed, if your returned object don't need interface constraints
string Value { get; set; }
}
public class PropertyMappingValue : IPropertyMappingValue
{
// Not relevant to this example, how is defined , is just an object, returned by our extension method
public string Value { get; set; }
}
public abstract class EntityBase
{
public static IPropertyMappingValue GetPropertyMappingValue<TEntity, TEntityDto>(string name) where TEntity : class, IRowVersion where TEntityDto : class, IEntityDto => EntityExtensions.GetPropertyMappingValue<TEntity, TEntityDto>(name);
}
// Sample Class
public class Entity : IRowVersion
{
}
// Sample Class
public class EntityDto : EntityBase, IEntityDto
{
}
public static class EntityExtensions
{
public static IPropertyMappingValue GetPropertyMappingValue<TEntityDto>(this TEntityDto instance, Type entityType, string name) where TEntityDto : class, IEntityDto
{
if (!typeof(IRowVersion).IsAssignableFrom(entityType))
throw new ArgumentException($"{entityType} do not implements {typeof(IRowVersion)}");
var method = typeof(EntityExtensions).GetMethod(nameof(GetPropertyMappingValue), new[] { typeof(string) });
var typeArgs = new[] { entityType, typeof(TEntityDto) };
var constructed = method?.MakeGenericMethod(typeArgs);
var result = constructed?.Invoke(null, new object[] { name });
return result as IPropertyMappingValue;
}
public static IPropertyMappingValue GetPropertyMappingValue<TEntity, TEntityDto>(string name) where TEntity : class, IRowVersion where TEntityDto : class, IEntityDto
{
//TO DO YOUR JOB HERE TO GET A VALID RETURNED OBJECT, as this is an example I will return a fake
// THE CODE IS JUST AN EXAMPLE of doing something with the types, but is not relevant for this example
//
var foo = typeof(TEntityDto);
var bar = typeof(TEntity);
//
return new PropertyMappingValue { Value = name }; // returning just a fake object
}
}
public class UnitTest
{
private readonly ITestOutputHelper _console;
public UnitTest(ITestOutputHelper console)
{
_console = console;
}
[Fact]
public void Test()
{
var oneWayOfExecuting = EntityBase.GetPropertyMappingValue<Entity, EntityDto>("Hello world"); //using a abstract base
_console.WriteLine(oneWayOfExecuting.Value);
var entityDto = new EntityDto();
var anotherWayOfExecuting = entityDto.GetPropertyMappingValue(typeof(Entity), "Hello world"); //using the extension method
_console.WriteLine(anotherWayOfExecuting.Value);
Assert.Equal("Hello world", oneWayOfExecuting.Value);
Assert.Equal("Hello world", oneWayOfExecuting.Value);
}

How to call non-generic methods inside generic types using reflection

Using .Net Framework 4.8.
I'm creating a shortcut system for my MDI WinForms application, so you can invoke methods when you press certain keys on certain forms, using custom attributes.
For context, the attributes look like this, and save them as Shortcutentry:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class ShortcutMethodAttribute : Attribute
{
public Keys[] Combination {get; set;}
public ShortcutMethodAttribute(params Keys[] combination)
{
Combination = combination;
}
}
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
public sealed class ShortcutTypeAttribute : Attribute
{
}
public class ShortcutEntry
{
public ShortcutMethodAttribute Attribute { get; private set; }
public object Object { get; set; }
public Keys[] KeyCombination { get; set; }
public MethodInfo MethodInfo { get; private set; }
public ShortcutEntry(object #object, Keys[] keyCombination, MethodInfo methodInfo, ShortcutMethodAttribute attrib)
{
this.Object = #object;
this.KeyCombination = keyCombination;
this.MethodInfo = methodInfo;
this.Attribute = attrib;
}
public void Trigger()
{
MethodInfo.Invoke(Object, null);
}
}
I resolve all shortcuts like this and save them as a Dictionary<Type, ShortcutEntry>:
public Dictionary<Type, List<ShortcutEntry>> RegisterAllAssemblyShortcuts()
{
var shortcuts = new Dictionary<Type, ShortcutEntry>();
var types = Assembly.GetExecutingAssembly().GetTypes();
var typesWithAttribute = types.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any());
foreach (var type in typesWithAttribute)
{
var methods = type.GetMethods().Where(x => x.GetCustomAttributes(typeof(ShortcutMethodAttribute), false).Length > 0);
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(typeof(ShortcutMethodAttribute), false).OfType<ShortcutMethodAttribute>();
if (attributes == null) continue;
foreach (var attribute in attributes)
{
var se = new ShortcutEntry(
null,
attribute.KeyCombination,
method,
attribute
);
if (!shortcuts.ContainsKey(type)) shortcuts.Add(type, new List<ShortcutEntry>);
shortcuts[type].Add(se);
}
}
}
return shortcuts;
}
To use it, you need to assign the ShortcutTypeAttribute to a type, and then ShortcutMethodAttribute to the method you want to call, with the key combination passed as parameter.
[ShortcutTypeAttribute]
public class SomeClass
{
public void SomeMethodA()
{
// do something
}
[ShortcutMethodAttribute(Keys.O, keys.I)]
public void SomeMethodB()
{
// do something
}
}
To summarize, it works like this:
Add ShortcutTypeAttribute to type containing the methods you want to call.
Add ShortcutMethodAttribute to the method to be called (with key combination).
Call RegisterAllAssemblyShortcuts()
Determine the type of the active MDI form.
Listen for keyboard input and check if shortcuts[mdiType] has any match.
If there is a ShortcutEntry then assing the Object and call ShortcutEntry.Trigger().
All of this steps work fine
The problem arises when I try to call a non-generic method with ShortcutEntry.Trigger() that is declared on a generic type, like so:
[ShortcutTypeAttribute]
public class KeyboundForm<T> : Form where T : class
{
[ShortcutMethodAttribute(Keys.O)]
public virtual void KeyOPressed() {}
}
The exception I get is:
System.InvalidOperationException: 'Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.'
I don't know why the MethodInfo for KeyOPressed() has MethodInfo.ContainsGenericParameters = true, when:
MethodInfo.IsGenericMethod = false
MethodInfo.IsGenericMethodDefinition = false
So I can't call MakeGenericMethod() on the KeyOPressed's MethodInfo
How can I invoke a non-generic method in a generic type?
Answer Edit: now it's working
Replaced the Trigger Function to recalculate methodinfo when it was generic.
public void Trigger()
{
if (MethodInfo.ContainsGenericParameters)
{
var type = Object.GetType();
var methodinfo = type.GetMethod(MethodInfo.Name);
methodinfo.Invoke(Object, null);
}
else
{
MethodInfo.Invoke(Object, null);
}
}
I don't know why the MethodInfo for KeyOPressed() has MethodInfo.ContainsGenericParameters == true`, when ...
This is because KeyOPressed is declared in a generic type. You need to create bound generic type (i.e. KeyboundForm<SomeActualForm>) to be able to invoke it.
One approach is to change your reflection to support only bound generic types:
var typesWithAttribute = types
.Where(t => !t.ContainsGenericParameters)
.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any())
Which will capture non-generic types like SomeClass and bound generic types like SomeOtherClass : KeyboundForm<SomeFormType> marked with corresponding attribute.
Or check for inherited attributes (GetCustomAttributes<ShortcutTypeAttribute>(true)) for classes which are bound generic types (Type.IsConstructedGenericType == true).
Related:
Invoke a non generic method with generic arguments defined in a generic class

Passing arguments to a method that need to be used for generic<T>?

I am currently working on a IoC container abstraction that sits on top of StructureMap. The idea is that it could work with other IoC containers as well.
public interface IRegister
{
IRegister RegisterType(Type serviceType, Type implementationType, params Argument[] arguments);
}
public abstract class ContainerBase : IRegister
{
public abstract IRegister RegisterType(Type serviceType, Type implementationType, params Argument[] arguments);
}
public class StructureMapContainer : ContainerBase
{
public StructureMapContainer(IContainer container)
{
Container = container;
}
public IContainer Container { get; private set; }
public override IRegister RegisterType(Type serviceType, Type implementationType, params Argument[] arguments)
{
// StructureMap specific code
Container.Configure(x =>
{
var instance = x.For(serviceType).Use(implementationType);
arguments.ForEach(a => instance.CtorDependency<string>(a.Name).Is(a.Value));
});
return this;
}
}
public class Argument
{
public Argument(string name, string value)
{
Name = name;
Value = value;
}
public string Name { get; private set; }
public string Value { get; private set; }
}
I can execute this code by doing the following:
//IContainer is a StructureMap type
IContainer container = new Container();
StructureMapContainer sm = new StructureMapContainer(container);
sm.RegisterType(typeof(IRepository), typeof(Repository));
The problem arises when I try to pass in constructor arguments. StructureMap allows you to fluently chain however many CtorDependency calls as you want but requires the constructor parameter name, value and type. So I could do something like this:
sm.RegisterType(typeof(IRepository), typeof(Repository), new Argument("connectionString", "myConnection"), new Argument("timeOut", "360"));
This code works as CtorDependency is currently of type string and both arguments are strings as well.
instance.CtorDependency<string>(a.Name).Is(a.Value)
How can I pass in multiple constructor arguments BUT of any type to this method? Is it possible? Any help would be much appreciated.
You could use a dynamic, anonymous object to pass in your arguments. Then use reflection to inspect the properties. The values can be any type.
dynamic example = new
{
a = 3,
b = "hello"
};
foreach (PropertyInfo p in example.GetType().GetProperties())
{
string key = p.Name;
dynamic value = p.GetValue(example, null);
Console.WriteLine("{0}: {1}", key, value);
}
This outputs
a: 3
b: hello
Just modify as necessary for your needs.
Edit:
Just so this is clear. Your registration method would take a dynamic parameter. It would be something like this. I don't have StructureMap, but this should get you in the right direction.
public override IRegister RegisterType(Type serviceType, Type implementationType, dynamic arguments)
{
// StructureMap specific code
Container.Configure(x =>
{
var instance = x.For(serviceType).Use(implementationType);
foreach (PropertyInfo p in arguments.GetType().GetProperties())
{
string key = p.Name;
object value = p.GetValue(arguments, null);
instance.CtorDependency<string>(key).Is(value));
}
});
return this;
}
Edit Part Deux:
Okay, so you're wanting to be able to pass in any data type for the key. Is this closer to what you're needing? You get the method by name, the turn it into a closed generic by calling MakeGenericMethod. Then you invoke it.
var method = instance
.GetType()
.GetMethod("CtorDependency")
.MakeGenericMethod(new Type[] { key.GetType() });
method.Invoke(instance, new object[] { key });

Dilemma in calling constructor of generic class

I have this generic singleton that looks like this:
public class Cache<T>
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
}
public T LoadFromSharePoint(Guid id)
{
return new T(id) // Here is the problem.
}
}
The error message is:
Cannot create an instance of type T because it does not have the new() constraint.
I have to mention that I must pass that id parameter, and there is no other way to do so. Any ideas on how to solve this would be highly appreciated.
Normally you would constrain the type T to a type that has a default constructor and call that. Then you'd have to add a method or property to be able to provide the value of id to the instance.
public static T LoadFromSharePoint<T>(Guid id)
where T : new() // <-- Constrain to types with a default constructor
{
T value = new T();
value.ID = id;
return value;
}
Alternatively since you specify that you have to provide the id parameter through the constructor, you can invoke a parameterized constructor using reflection. You must be sure the type defines the constructor you want to invoke. You cannot constrain the generic type T to types that have a particular constructor other than the default constructor. (E.g. where T : new(Guid) does not work.)
For example, I know there is a constructor new List<string>(int capacity) on List<T>, which can be invoked like this:
var type = typeof(List<String>);
object list = Activator.CreateInstance(type, /* capacity */ 20);
Of course, you might want to do some casting (to T) afterwards.
To do this you should specify what T is. Your Cache<T> can hold anything? Tiger, Fridge and int as well? That is not a sound design. You should constrain it. You need an instance of T which will take a Guid to construct the instance. That's not a generic T. Its a very specific T. Change your code to:
public class Cache<T> where T : Cacheable, new()
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
//you're first checking for presence, and then adding to it
//which does the same checking again, and then returns the
//value of key again which will have to see for it again.
//Instead if its ok you can directly return
//return cachedBlocks[id] = LoadFromSharePoint(id);
//if your LoadFromSharePoint is not that expensive.
//mind you this is little different from your original
//approach as to what it does.
}
public T LoadFromSharePoint(Guid id)
{
return new T { Key = id }; // Here is no more problem.
}
}
public interface Cacheable
{
Guid Key { get; set; }
}
Now derive all the cacheables (whatever Ts that you will pass it for Cache<T>) from the interface Cacheable.
In order to use the constructor of a Generic Type without any constraint, and within the class, the syntax where T : class, new() needs to be used
This enables to change values of attributes (fields) - not only get/set properties) at runtime depending the target class used
First, declaring the generic class:
public class Foo<T> where T : class, new()
{
public T oneEmptyElement()
{
return new T();
}
public T setAttribute(string attributeName, string attributeValue)
{
T objT = new T();
System.Reflection.FieldInfo fld = typeof(T).GetField(attributeName);
if (fld != null)
{
fld.SetValue(objT, attributeValue);
}
return objT;
}
public List<T> listOfTwoEmptyElements()
{
List<T> aList = new List<T>();
aList.Add(new T());
aList.Add(new T());
return aList;
}
}
Declare then a potential target class:
public class Book
{
public int name;
}
And finally the call can be done like this:
Foo<Book> fooObj = new Foo<Book>();
Book aBook = fooObj.oneEmptyElement();
aBook.name = "Emma";
Book anotherBook = fooObj.setAttribute("name", "John");
List<Book> aListOfBooks = fooObj.listOfTwoEmptyElements();
aListOfBooks[0].name = "Mike";
aListOfBooks[1].name = "Angelina";
Console.WriteLine(aBook.name); //Output Emma
Console.WriteLine(anotherBook.name); //Output John
Console.WriteLine(aListOfBooks[0].name); // Output Mike
Console.WriteLine(aListOfBooks[1].name); // Output Angelina

Cannot provide arguments when creating an instance of generic type

I have an object that I want to have read only after it is created... namely because the properties in the constructor must be used in GetHashCode, and therefore can't change once created.
I this is one of many classes that are readonly:
public class AccountLabel
{
private string result;
public AccountLabel(string result)
{
// TODO: Complete member initialization
this.result = result;
}
public string JSONRPCData { get { return this.result; } }
}
I have a generic result class like this
public class JsonResult<T> where T : JObject, new()
{
private T bhash;
private string p;
private JsonErrorResponse error;
private int _id;
private Newtonsoft.Json.Linq.JObject ret;
public JsonResult(Newtonsoft.Json.Linq.JObject ret)
{
this.ret = ret;
var tempError = ret["error"];
var tempid = ret["id"];
JsonErrorResponse error = new JsonErrorResponse(tempError);
this.error = error;
this._id = 1;
var tempresult = ret["result"];
T someResult = new T(tempresult); // <--- here is my problem
}
My problem is that I want to pass an object into T's constructor but can't. When I type this the compiler tells me Cannot provide arguments when creating an instance of variable type
What is the best way to work around this situation?
Should I have an Interface that I can call that will update the property?
Will that previous Interface break encapsulation or allow changes to be made to my object?
How else should I approach this?
You can remove the new type constraint, and use Activator.CreateInstance instead.
Instead of this
T someResult = new T(tempresult);
write this:
T someResult = (T)Activator.CreateInstance(
typeof(T)
, new object[] { tempresult }
);
This is likely to be somewhat slower because of going through reflection, and the static checks by the compiler would not be performed. However, considering your scenario, it does not look like either of these would present a significant problem.
You could pass in a factory delegate to the constructor of JSonResult<T>:
public class JsonResult<T> where T : JObject
{
public JsonResult(Newtonsoft.Json.Linq.JObject ret, Func<object, T> factory)
{
var tempresult = ret["result"];
T someResult = factory(tempresult);
}
}
object in Func<object, T> could be replaced with whatever the type of tempResult actually is.

Categories