So I have a class that is a generic and it may need to, inside a method of its own, create an instance of itself with a different kind of generic, whose type is obtained through reflection.
This is important because this Repository maps T to a database table [it's an ORMish I am writing] and if the class that represents T has a collection representing ANOTHER table I need to be able to instance that and pass it to the repository [ala Inception].
I'm providing the method in case it makes it easier to see the problem.
private PropertiesAttributesAndRelatedClasses GetPropertyAndAttributesCollection()
{
// Returns a List of PropertyAndAttributes
var type = typeof(T);
//For type T return an array of PropertyInfo
PropertiesAttributesAndRelatedClasses PAA = new PropertiesAttributesAndRelatedClasses();
//Get our container ready
//Let's loop through all the properties.
PropertyAndAttributes _paa;
foreach(PropertyInfo Property in type.GetProperties())
{
//Create a new instance each time.
_paa = new PropertyAndAttributes();
//Adds the property and generates an internal collection of attributes for it too
_paa.AddProperty(Property);
bool MapPropertyAndAttribute = true;
//This is a class we need to map to another table
if (Property.PropertyType.Namespace == "System.Collections.Generic")
{
PAA.AddRelatedClass(Property);
//var x = Activator.CreateInstance("GenericRepository", Property.GetType().ToString());
}
else
{
foreach(var attr in _paa.Attrs)
{
if (attr is IgnoreProperty)
{
//If we find this attribute it is an override and we ignore this property.
MapPropertyAndAttribute = false;
break;
}
}
}
//Add this to the list.
if (MapPropertyAndAttribute) PAA.AddPaa(_paa);
}
return PAA;
}
So given
GenericRepository<T>, and I want to make a GenericRepository<string type obtained via reflection from the Property> how would I do this?
The line I need to replace with something that WORKS is:
//var x = Activator.CreateInstance("GenericRepository", Property.GetType().ToString());
Thanks.
I think you're looking for the MakeGenericType method:
// Assuming that Property.PropertyType is something like List<T>
Type elementType = Property.PropertyType.GetGenericArguments()[0];
Type repositoryType = typeof(GenericRepository<>).MakeGenericType(elementType);
var repository = Activator.CreateInstance(repositoryType);
Activator.CreateInstance(typeof(GenericRepository<>).MakeGenericType(new Type[] { Property.GetTYpe() }))
Related
I am currently working on a scenario where I would need the Automapper to use IOC to create new instances. Additionally, I would need to register the mapping for Automapper only when the requirement rises (so i am not using Profile Files). In order the achieve the same, I have done the following.
var cfg = new MapperConfigurationExpression();
cfg.ConstructServicesUsing(type =>
{
return CreateInstanceUsingIOC(type);
});
cfg.CreateMap<Model1, Model2>()
.ConstructUsingServiceLocator();
var config = new MapperConfiguration(cfg);
var mapper = config.CreateMapper();
var model1UsingIoC = CreateModel1UsingIoC();
model1UsingIoC.MyProfile = new Person();
model1UsingIoC.MyProfile.FirstName = "New First Name";
model1UsingIoC.MyProfile.LastName = "New Last Name";
model1UsingIoC.CommonProperty = "This wont be copied";
var model2b = mapper.Map<Model2>(model1UsingIoC);
This works as desired, however, problem lies with the inner property Model1.MyProfile. The instance of MyProfile in Source and Destination looks to be the same.
ReferenceEquals(model2b.MyProfile,model1UsingIoC.MyProfile) // True
In addition, I would like to create each of the User Defined sub properties using IoC. For the purpose, I added modified the ConstructServicesUsing statement as
cfg.ConstructServicesUsing(type =>
{
var instance = CreateInstance(type);
return ReassignProperties(instance);
});
Where ReassignProperties is defined as
public static object ReassignProperties(object obj)
{
if (obj == null) return null;
Type objType = obj.GetType();
PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
// Check if its a collection
if (property.IsEnumerable())
{
if (property.GetValue(obj, null) is IEnumerable elems)
{
foreach (var item in elems)
{
ReassignProperties(item);
}
}
}
else
{
// If it is User Defined Type, Recursively loop again
if (property.PropertyType.Assembly == objType.Assembly)
{
var newValue = CreateInstance(property.PropertyType);
property.SetValue(obj, newValue);
var value = property.GetValue(obj);
ReassignProperties(value);
}
else
{
var propValue = property.GetValue(obj, null);
Console.WriteLine($"{property.Name} = {propValue}");
}
}
}
return obj;
}
But as one could assume, that didn't help either and while mapping, the same instance is being copied over for subproperties. Could someone guide me on how to ensure a new instance is created for user defined sub properties when using Automapper (ie, new instance are created and values are copied over unless the type is primitive type) ?
Update 001
As suggested by Lucian, i tried creating a custom mapper as follows and add it to Mapping Registery.
public class NewInstanceMapper : IObjectMapper
{
public bool IsMatch(TypePair context) => true;
public static object CreateInstance(Type type)
{
try
{
var methodInfo = typeof(IoC).GetMethod(nameof(IoC.Get), BindingFlags.Public | BindingFlags.Static);
var genericMethodInfo = methodInfo.MakeGenericMethod(type);
return genericMethodInfo.Invoke(null, new object[] { null });
}
catch (Exception)
{
return Activator.CreateInstance(type);
}
}
public Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
{
return Expression.Call(this.GetType(), nameof(CreateInstance), null, Expression.Constant(destExpression.Type));
}
}
And added it to MappingRegistery using
var cfg = new MapperConfigurationExpression();
cfg.Mappers.Insert(0,new NewInstanceMapper());
But, as one could guess, this created new instance, but didn't copy the value
Additional Notes
Please note what I would like to achieve would be
a) Each property values are copied over to destination based on naming conventions
b) Each destination property (exception for string) are new instance
each created using IoC, and are not the same instances of source property
c) Point b includes nested properties, collections (each element in collection needs to be created using ioc and sub property values values copied over)
Creating a map for your Person class to itself will ensure that instances are deep copied when mapped:
cfg.CreateMap<Person, Person>();
If any of your sub-properties are also reference types that need to be deep copied, you can follow the same strategy and define self-maps for them too.
This is my code
public virtual T GetRepository<T>()
where T : class
{
var type = typeof ( T );
var properties = this.GetType().GetProperties();
foreach ( var property in properties )
{
var name = property.Name;
if ( name == type.Name )
{
var a = this.GetType().GetProperty(name) as T;
return a;
}
}
return null;
}
The objective is to return the generic type T. First I get the properties of the class, and then I loop over the properties in hopes of finding a property name which has the same name as the type T.
This works okay in my case because the properties are all of the form Interface<FooRepository> FooRepository { get; set; } so I need only compare the two.
This method is inside a class. The problem is that a will change to null immediately after the statement as T. What can be done?
If you want the value of the property you could try using the GetValue method and replace this:
var a = this.GetType().GetProperty(name) as T;
with this:
var a = property.GetValue(this) as T;
Also it is not quite clear what exactly is the purpose of such method but this condition here looks shaky:
if (name == type.Name)
The name of the property should equal the name of the generic type parameter. Is it really what is needed here?
GetProperty method will always return ProprtyInfo type so unless T is PropertyInfo a will be always null.
I think I have a lack of understand, what exactly is happening:
The user can enter a Path to an Assembly and an Object type and I try to create a instance of it.
My approach:
Assembly a = Assembly.LoadFile(txtAssemblyPath.Text);
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
var obj = Activator.CreateInstance<myType>();
var obj2 =(myType) Activator.CreateInstance(myType);
The problem is in the Creation of the Object itself. It seems like the myType is not threated as a Type. On this example: Creating generic variables from a type - How? Or use Activator.CreateInstance() with properties { } instead of parameters ( )?
They just get an object, so I guess this is not the same Case. What I dont understand at all: CreateInstance(Type) works, but CreateInstance with the Type doesn't, but T and Type should be the same: System.Type.
Thanks in advance for clarifying.
Matthias
There are a using difference... When you write:
var obj = Activator.CreateInstance<myType>();
You used your Class like a Type, and it's the good way to do it.
You used a generic type who have a reference to a class type.
But there:
var obj2 =(myType) Activator.CreateInstance(myType);
You used you class like an instance (object). And you can't do that, a class is a schema.
If you want to call the second method, you have to write:
var obj2 =(myType) Activator.CreateInstance(typeof(myType));
This code will create an instance of the class Type, and this instance will describe your class myType.
I hope to be clear.
A class is a schema, you can create an object with this schema, it will be an instance (a memory-object of your class).
When you use generic type of method such as Activator.CreateInstance<T>(); you have to provide Strong Type of T. That means you have to pass known type name instead of T. for example:
var activatorPerson = (Person)Activator.CreateInstance<Person>();
That's why there is a non generic form of Activator.CreateInstance(typeGoesHere) function which can be used in cases that we do not have a Strong Type at moment of creation of an object. So we can pass type as a parameter to that function. we can provide type variable in lots of ways. In your case you find the proper type in your assembly as following:
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
also you have to notice that explicit casting as you typed in your code is not valid:
obj2 =(myType) Activator.CreateInstance(myType);
because you have to provide Strong Type name for explicit casting.
when we do not have access to strong type names at run time, we have to use non generic versions of methods.
Here is the pure dynamic way.
This is the factory class and dynamic instance create method:
public class RepositoryFactory
{
public static dynamic CreateDynamic<TEntity>() where TEntity : BaseEntity
{
dynamic repositoryInstance = null;
var subRepositories = AssemblyHelper.GetSubclassesOf(typeof(BaseRepository<TEntity>), true);
var entityTypeName = typeof(TEntity).Name;
var subRepository = subRepositories.FirstOrDefault(x => x.Name == entityTypeName + "Repository");
if (subRepository != null)
{
var repositoryType = subRepository.UnderlyingSystemType;
repositoryInstance = Activator.CreateInstance(repositoryType);
}
return repositoryInstance;
}
}
This is the helper class for mapping types between Entity and Repository.
public static class AssemblyHelper
{
public static List<Type> GetSubclassesOf(Type type, bool ignoreSystem)
{
List<Type> lReturn = new List<Type>();
foreach (var a in System.Threading.Thread.GetDomain().GetAssemblies())
{
if (ignoreSystem && a.FullName.StartsWith("System."))
{
continue;
}
foreach (var t in a.GetTypes())
{
if (t.IsSubclassOf(type) || (type.IsInterface && t.GetInterfaces().FirstOrDefault(e => e.FullName == type.FullName) != null))
{
lReturn.Add(t);
}
}
}
return lReturn;
}
}
This is the Manager class for use case:
public class PageManager
{
private readonly ContentPageRepository _pageRepository;
public PageManager()
{
_pageRepository = RepositoryFactory.CreateDynamic<ContentPage>();
}
public void GetPagesByContentType(ContentType type)
{
var pages = _pageRepository.GetByPredicate(x => x.Status != EntityStatus.Deleted && x.Node.Type == type);
foreach (var page in pages)
{
//Deal with it :)
}
}
}
I have a List that I am iterating through.
Inside the List<> are Argument classes which contain two properties 'PropertyName' and 'Value'
What I need to do is iterate through the collection of Arguments and assign the Value of that Argument to the Property (with the same name as current Argument) of a different class.
Example:
Argument:
PropertyName: ClientID
Value: 1234
Members Class:
ClientID = {Argument Value here}
I hope this makes sense. I have a way of doing it, hard coding the properties of my class and matching it up with the Argument list.
Something like:
foreach(var arg in List<Argument>)
{
Members.ClientID = arg.Find(x => compareName(x, "ClientID")).Value;
//where compareName just does a simple string.Compare
}
But what would the BEST way be for something like this?
EDIT: Sorry about this guys and thanks for the replies so far. Here is what I didn't mention and might make a difference.
Each argument is a different property for the same class. I am iterating through the List and each one in there will be for the same Members class I have to populate.
I wanted to mention this because im thinking in the foreach I might have to use a switch to determine what 'PropertyName' I have for that Argument. ClientID is one of them but I believe there are 14 total properties in the Members class that need populated from the Collection.
Does that change things?
Thanks again
public object this[string propertyName]
{
get
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
Then you can get/set properties within the class using
myClassInstance["ClientId"] = myValue;
If I understand what you're asking, perhaps something like this will work for you:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value);
Members.ClientID = argDict["ClientID"];
...
If you need to do some special comparison on the keys you can provide the dictionary it's own IEqualityComparer. For example, this will make sure that the keys are treated case-insensitively:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value,
StringComparer.OrdinalIgnoreCase);
This will work fine as long as the arguments list contains all the values you need. If some arguments might be missing, you'd have to do something like this:
if (argDict.ContainsKey("ClientID")) {
Members.ClientID = argDict["ClientID"];
}
Or possibly something like this:
Members.ClientID = argDict.ContainsKey("ClientID") ? argDict["ClientID"] : "DefaultValue";
I think that your basic intent is to set the value of a property on a target object based on the property name. Since you did not provide the Argument class I will assume it is defined like this:
public class Argument
{
public string PropertyName{get; set;}
public object PropertyValue{get;set;}
}
Further assume you have the class Blah defined like this:
public class Blah
{
public string AString{get; set;}
public int AnInt{get; set;}
public DirectoryInfo ADirInfo{get; set;}
}
If you wish to assign to the properties of a Blah object based on the values in List<Argument> you can do so like this:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = new DirectoryInfo(#"c:\logs")}
};
Blah b = new Blah();
Type blahType = b.GetType();
foreach(Argument arg in arguments)
{
PropertyInfo prop = blahType.GetProperty(arg.PropertyName);
// If prop == null then GetProperty() couldn't find a property by that name. Either it doesn't exist, it's not public, or it's defined on a parent class
if(prop != null)
{
prop.SetValue(b, arg.PropertyValue);
}
}
This depends on the objects stored in Argument.PropertyValue having the same type as the property of Blah referred to by Argument.PropertyName (or there must be an implicit type conversion available). For example, if you alter the List<Argument> as follows:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = "foo"}
};
you will now get an exception when attempting to assign to Blah.ADirInfo: Object of type 'System.String' cannot be converted to type 'System.IO.DirectoryInfo'
Please have a look at below code.
Issue is at following loc.
MyClassExample obj2 = lstObjectCollection[0] as type;
I want to type cast an object of list to its type. But type will be given at runtime.
How can we cast an object, knowing its type at runtime?
class RTTIClass
{
public void creatClass()
{
// Orignal object
MyClassExample obj1 = new MyClassExample {NUMBER1 =5 };
// Saving type of original object.
Type type = typeof(MyClassExample);
// Creating a list.
List<object> lstObjectCollection = new List<object>();
// Saving new object to list.
lstObjectCollection.Add(CreateDuplicateObject(obj1));
// Trying to cast saved object to its type.. But i want to check its RTTI with type and not by tightly coupled classname.
// How can we achive this.
MyClassExample obj2 = lstObjectCollection[0] as type;
}
public object CreateDuplicateObject(object originalObject)
{
//create new instance of the object
object newObject = Activator.CreateInstance(originalObject.GetType());
//get list of all properties
var properties = originalObject.GetType().GetProperties();
//loop through each property
foreach (var property in properties)
{
//set the value for property
property.SetValue(newObject, property.GetValue(originalObject, null), null);
}
//get list of all fields
var fields = originalObject.GetType().GetFields();
//loop through each field
foreach (var field in fields)
{
//set the value for field
field.SetValue(newObject, field.GetValue(originalObject));
}
// return the newly created object with all the properties and fields values copied from original object
return newObject;
}
}
class MyClassExample
{
public int NUMBER1 {get; set;}
public int NUMBER2{get; set;}
public int number3;
public int number4;
}
The pattern I normally use is the is operator which will tell whether your object is a particular type. This will work if you already kinda sorta know which objects you will be using
Object myObject
if(myObject is Obj1)
// do obj1 stuff
else if(myObject is Obj2)
// do stuff with obj2
I've never had it come up where i had to operate on more than a handful of different types and treat them all specially, so this is what i normally do.
You can easily get all the objects of a certain type in the list using the OfType<T> extension method:
lstObjectCollection.OfType<MyClassExample>()
If the type is only known at runtime, you can do this:
lstObjectCollection.Where(o => o.GetType() == type)