Copy properties between objects using reflection and extesnion method - c#

This is my code where I create a "copy" of one object (Entity) into a custom object.
It copies just properties with the same name in both source and target.
My problem is when an Entity has a navgiaton to another Entity, for this case I added a custom attribute that I add above the property in the custom class.
For example the custom class looks like:
public class CourseModel:BaseDataItemModel
{
public int CourseNumber { get; set; }
public string Name { get; set; }
LecturerModel lecturer;
[PropertySubEntity]
public LecturerModel Lecturer
{
get { return lecturer; }
set { lecturer = value; }
}
public CourseModel()
{
lecturer = new LecturerModel();
}
}
The problem is in targetProp.CopyPropertiesFrom(sourceProp); line, when I try to call extension method again (to copy the nested object) ,because the type is determined on run time, extension method couldn't resolved on compile time.
Maybe I am missing something...
public static void CopyPropertiesFrom(this BaseDataItemModel targetObject, object source)
{
PropertyInfo[] allProporties = source.GetType().GetProperties();
PropertyInfo targetProperty;
foreach (PropertyInfo fromProp in allProporties)
{
targetProperty = targetObject.GetType().GetProperty(fromProp.Name);
if (targetProperty == null) continue;
if (!targetProperty.CanWrite) continue;
//check if property in target class marked with SkipProperty Attribute
if (targetProperty.GetCustomAttributes(typeof(SkipPropertyAttribute), true).Length != 0) continue;
if (targetProperty.GetCustomAttributes(typeof(PropertySubEntity), true).Length != 0)
{
//Type pType = targetProperty.PropertyType;
var targetProp = targetProperty.GetValue(targetObject, null);
var sourceProp = fromProp.GetValue(source, null);
targetProp.CopyPropertiesFrom(sourceProp); // <== PROBLEM HERE
//targetProperty.SetValue(targetObject, sourceEntity, null);
}
else
targetProperty.SetValue(targetObject, fromProp.GetValue(source, null), null);
}
}

You'll have to cast first.
((BaseDataItemModel)targetProp).CopyPropertiesFrom(sourceProp);

Either you need to cast targetProperty to BaseDataItemModel so you can call the extension method on it (edit: as in agent-j's answer), or otherwise you could just forget about that base class. Why does your reflection algorithm need it? It could work on any class, and is directed purely by the attributes on properties.
And if it works on any object, it shouldn't be an extension method.

Related

Instantiate an instance of a Type including all object properties and adhere to inheritance

I'm not sure if that title is reflective of the actual question, so let me explain. Is there a way to instantiate a class and recursively instantiate all properties that are classes?
For example :
public class Base
{
public int BaseValue{ get; set;}
}
public class Extended : Base
{
public int ExtendedValue{ get; set;}
public AnotherExtendedClass AnotherClass { get; set;}
}
I would like to create a json payload comprised of an empty instance of Extended with all default values and properties instantiated. And use it like:
string representation = Test.CreateDefaultEmptyJson(Extended);
public static string CreateDefaultEmptyJson(Type type)
{
JsonSerializerSettings settings = new JsonSerializerSettings().Configure();
var defaultInstance= Activator.CreateInstance(type);
return JsonConvert.SerializeObject(defaultInstance, settings);
}
The output does not include the Extended class properties. I get back :
{
"BaseValue":0
}
When I would really like to see ( or something similar ):
{
"BaseValue":0,
{
"ExtendedValue":0,
{
...
}
}
}
I suppose I could recursively iterate all types of Extended and call the default constructor, however, before I go down that road there may be a few lines of code to accomplish the same.
As far as I know there is not a built-in way to do this short of writing your own recursive method.
However, assuming that:
your classes all have parameterless (default) constructors,
the non-primitive properties are all concrete types (not interfaces), and
you don't have any reference loops in your class structure,
then you can create such a method in about a dozen lines of code:
public static string CreateDefaultEmptyJson(Type type)
{
return JsonConvert.SerializeObject(RecursiveCreateInstance(type), Formatting.Indented);
}
public static object RecursiveCreateInstance(Type type)
{
object obj = null;
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor != null)
{
obj = ctor.Invoke(null);
foreach (PropertyInfo prop in type.GetProperties())
{
Type propType = prop.PropertyType;
if (prop.CanWrite && propType.IsClass)
{
prop.SetValue(obj, RecursiveCreateInstance(propType));
}
}
}
return obj;
}
Fiddle: https://dotnetfiddle.net/3VMTsC
If the above assumptions don't hold, then things get complicated fast. If you're running into issues where you need an easy way to create fake objects for testing purposes, then you might want to look into using a mocking framework.
This hastily-written class begins to address your question. It returns the settable properties which return reference types and walks through them recursively, creating instances as needed.
It doesn't cover
Indexed properties
Depth of recursion
You may be better off just setting defaults on the properties themselves so that the class won't be created with undesirable nulls.
public class PropertyPopulator
{
public void PopulateProperties(object target)
{
var properties = target.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.PropertyType.IsClass && p.CanWrite && p.CanRead);
foreach (var property in properties)
{
var propertyValue = property.GetValue(target);
if (propertyValue == null)
{
var constructor = property.PropertyType.GetConstructor(new Type[] { });
if (constructor != null)
{
propertyValue = constructor.Invoke(new object[] { });
property.SetValue(target, propertyValue);
PopulateProperties(propertyValue);
}
}
}
}
}

getting the property type in a generic class (Mono.Cecil)

I am using Mono.Cecil to automatically generate (lots of, simple, generic) factory methods providing a convenient API for a library. The factories are generated for properties marked with a special custom attribute. To generate them, I must know the type of such property. The non-generic case is simple:
ModuleDefinition module = /* obtained from ReadAssembly */
foreach (var type in module.Types)
if (/* type is marked with the right attribute */)
foreach (var prop in type.Properties)
if (/* prop is marked with the right attribute */)
GenerateFactory(type, prop, prop.PropertyType);
However, some of the types marked are in fact generics. In this case, the attribute on the type contains the generic arguments for which the factory should be made, like this:
[EmitFactories(typeof(int))]
public class Class<T>
{
[MagicProperty]
T Property { get; set; }
}
(here, I want the factory to be made for Class<int>.Property).
I am handling this case by making type a GenericInstanceType. However, I cannot get to the type of the property -- to enumerate type.Properties I need to first call Resolve(), which loses all generic information. The property type is then T (instead of int), which of course makes later code fail miserably.
Mono.Cecil has GenericInstanceType and GenericInstanceMethod, but there is no equivalent for properties. I tried using module.Import(prop.PropertyType, type) (giving type as the generic parameter provider), but this doesn't work.
Do you have any ideas on how I can resolve the actual property type? Note, that it can be completely unrelated to T, T itself, or have T burried inside (e.g., List<T>). Ideally, it would work given type as a TypeReference -- this way I would not have to write separate code for the non-generic and generic cases.
Based on https://stackoverflow.com/a/16433452/613130, that is probably based on https://groups.google.com/d/msg/mono-cecil/QljtFf_eN5I/YxqLAk5lh_cJ, it should be:
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Rocks;
namespace Utilities
{
public class EmitFactoriesAttribute : Attribute
{
public readonly Type[] Types;
public EmitFactoriesAttribute()
{
}
public EmitFactoriesAttribute(params Type[] types)
{
Types = types;
}
}
public class MagicPropertyAttribute : Attribute
{
}
public static class EmitFactories
{
public static void WorkOnAssembly(string path)
{
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("ConsoleApplication4.exe");
ModuleDefinition module = assembly.MainModule;
TypeDefinition emitFactoriesAttribute = module.Import(typeof(EmitFactoriesAttribute)).Resolve();
TypeDefinition magicPropertyAttribute = module.Import(typeof(MagicPropertyAttribute)).Resolve();
foreach (TypeDefinition type in module.Types)
{
CustomAttribute emitFactory = type.CustomAttributes.SingleOrDefault(x => x.AttributeType.MetadataToken == emitFactoriesAttribute.MetadataToken);
if (emitFactory == null)
{
continue;
}
TypeReference typeRef = type;
TypeReference[] replacementTypes;
if (emitFactory.ConstructorArguments.Count != 0)
{
var temp = ((CustomAttributeArgument[])emitFactory.ConstructorArguments[0].Value);
replacementTypes = Array.ConvertAll(temp, x => (TypeReference)x.Value);
}
else
{
replacementTypes = new TypeReference[0];
}
if (replacementTypes.Length != type.GenericParameters.Count)
{
throw new NotSupportedException();
}
if (replacementTypes.Length != 0)
{
typeRef = typeRef.MakeGenericInstanceType(replacementTypes);
}
foreach (PropertyDefinition prop in type.Properties)
{
CustomAttribute magicProperty = prop.CustomAttributes.SingleOrDefault(x => x.AttributeType.MetadataToken == magicPropertyAttribute.MetadataToken);
if (magicProperty == null)
{
continue;
}
MethodReference getter = prop.GetMethod;
MethodReference setter = prop.SetMethod;
if (replacementTypes.Length != 0)
{
if (getter != null)
{
getter = getter.MakeHostInstanceGeneric(replacementTypes);
}
if (setter != null)
{
setter = setter.MakeHostInstanceGeneric(replacementTypes);
}
}
}
}
}
}
public static class TypeReferenceExtensions
{
// https://stackoverflow.com/a/16433452/613130
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, params TypeReference[] arguments)
{
var reference = new MethodReference(self.Name, self.ReturnType, self.DeclaringType.MakeGenericInstanceType(arguments))
{
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis,
CallingConvention = self.CallingConvention
};
foreach (var parameter in self.Parameters)
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
foreach (var generic_parameter in self.GenericParameters)
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
return reference;
}
}
// Test
[EmitFactories(typeof(int), typeof(long))]
public class Class<TKey, TValue>
{
[MagicProperty]
Dictionary<TKey, TValue> Property1 { get; set; }
[MagicProperty]
List<TValue> Property2 { get; set; }
}
}
You hadn't defined how EmitFactoriesAttribute was, so I have written it as a EmitFactoriesAttribute(params Type[] types), to be able to accept multiple substitutions for cases like Class<TKey, TValue>.
In the end I'm not manipulating directly the property: I'm manipulating its getter and setter.
I'm not able to test it, because I don't have the factory...

Get PropertyInfo of a parameter passed as lambda expression | Compact Framework, C#

Say I have following Asset class:
class Asset
{
public int Id { get; set; }
public string Name { get; set; }
}
Now I want to write a method GetPropertyInfo(a=>a.Name); and this method gives me the PropertyInfo of Asset.Name. I should be able to call this method like:
EDIT Example Method Call
PropertyInfo propInfo = GetPropertyInfo(a=>a.Name);
I have a List<PropertyInfo> so I want to match a given lambda expression with those in my list.
if(Possible on Compact Framework 3.5 && using C#)
How?
else
Please Notify
Thanks.
This can be done under .NETCF 3.5.
private List<Asset> m_list;
private Asset[] GetPropertyInfo(string name) {
var items = m_list.Where(a => a.Name == name);
if (items != null) {
return items.ToArray();
} else {
return null;
}
}
You will, however, need to initialize the m_list and fill it with your data first.
UPDATE:
So, your list is of type PropertyInfo and you want a call to get the type that matches a particular Asset object.
If that is correct, you could simply edit the code above to be as follows:
private List<PropertyInfo> m_list;
private PropertyInfo GetPropertyInfo(Asset a) {
return m_list.FirstOrDefault(x => x.Name == a.Name);
}
I am not sure how you are getting the List<PropertyInfo>, though. I was able to pull a single PropertyInfo object using the code below:
private PropertyInfo GetPropertyInfo() {
var t = Type.GetType("System.Reflection.MemberInfo");
return t.GetProperty("Name");
}
There was nothing useful in this item.

LINQ Changes Not Recognized When Using Generics

I am currently trying to create a Class which employs generics in order to reduce the amount of work needed for future development. As I add Tables to the LINQ To SQL Designer, certain basic Methods are used in each one. Rather than reproduce them in every Partial Class associated with every new Table, I would like to employ a single generic Class. The issue is that any changes made to the Entities are not recognized and therefore not submitted.
Public Partial Class ABC
{
Public Static Bool Synchronize(string source, string destination)
{
try
{
DataContext destinationDB = DataConnection.Destination(destination);
Table<ABC> destinationABCs = destinationDB.ABCs;
DataContext sourceDB = DataConnection.Destination(source)
Table<ABC> sourceABCs = sourceDB.ABCs;
foreach (ABC ABCCode in sourceABCs)
{
ABC destABCCode = destinationABCs.SingleOrDefault(x => x.Id == ABCCode.Id);
bool same = EntityProcessing.AreIdentical(ABCCode, destABCCode);
if (same == false)
{
destABCCode = (ABC)EntityProcessing.Synchronize(ABCCode, destABCCode);
}
}
ChangeSet test = destinationDB.GetChangeSet(); // Test Line For Debugging
destinationDB.SubmitChanges();
}
return true;
}
}
The next Class is:
Public Static Class EntityProcessing
{
Public Static Bool AreIdentical(Object sourceEntity, Object destinationEntity)
{
if (sourceEntity.GetType() == destinationEntity.GetType())
{
Type t = sourceEntity.GetType();
FieldInfo[] tList = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo fi in tList)
{
if ((fi.GetValue(sourceEntity) != null ? fi.GetValue(sourceEntity).ToString()
: null) == (fi.GetValue(destinationEntity) != null ?
fi.GetValue(destinationEntity).ToString() : null))
{ continue; }
else
{ return false; }
}
return true;
}
else
{ return false; }
}
Public Static Object Synchronize(Object sourceEntity, Object destinationEntity)
{
if (sourceEntity.GetType() == destinationEntity.GetType())
{
Type t = sourceEntity.GetType();
FieldInfo[] tList = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo fi in tList)
{
fi.SetValue(destinationEntity, fi.GetValue(sourceEntity));
}
}
return destinationEntity;
}
}
I have tried modifying the EntityProcessing.Synchronize method into a Void method as well. Neither works. Both will return the correct Entity with the Fields set to the appropriate results. The issue lies in the fact that LINQ does not recognize the Entities as having changed.
If I add a temporary line of ChangeSet test = destinationDB.GetChangeSet();, the Updated count is zero. The loss appears to be in the conversion to Objects.
I have tried setting the Parameter Type to ABC on the EntityProcessing.Synchronize() method and modifying a Field, and the Updated count in test is correct. How do I resolve this?
How do I submit the updated entities to the database or rather, how do I get LINQ to recognize these entities are being changed and needing an update?
Do you mean: Public Static Bool Synchronize<ABC>(string source, string destination) with "ABC" as the generic type?
However, I don't think your .ABCs will work that simply. You may have to use reflection to get at the proeprty with that particular name. For example, first use reflection to get the name of the type parameter (ABC), and then use reflection to get the table field from the data source based on this type name.

How to check if property setter is public

Given a PropertyInfo object, how can I check that the setter of the property is public?
Check what you get back from GetSetMethod:
MethodInfo setMethod = propInfo.GetSetMethod();
if (setMethod == null)
{
// The setter doesn't exist or isn't public.
}
Or, to put a different spin on Richard's answer:
if (propInfo.CanWrite && propInfo.GetSetMethod(/*nonPublic*/ true).IsPublic)
{
// The setter exists and is public.
}
Note that if all you want to do is set a property as long as it has a setter, you don't actually have to care whether the setter is public. You can just use it, public or private:
// This will give you the setter, whatever its accessibility,
// assuming it exists.
MethodInfo setter = propInfo.GetSetMethod(/*nonPublic*/ true);
if (setter != null)
{
// Just be aware that you're kind of being sneaky here.
setter.Invoke(target, new object[] { value });
}
.NET properties are really a wrapping shell around a get and set method.
You can use the GetSetMethod method on the PropertyInfo, returning the MethodInfo referring to the setter. You can do the same thing with GetGetMethod.
These methods will return null if the getter/setter is non-public.
The correct code here is:
bool IsPublic = propertyInfo.GetSetMethod() != null;
public class Program
{
class Foo
{
public string Bar { get; private set; }
}
static void Main(string[] args)
{
var prop = typeof(Foo).GetProperty("Bar");
if (prop != null)
{
// The property exists
var setter = prop.GetSetMethod(true);
if (setter != null)
{
// There's a setter
Console.WriteLine(setter.IsPublic);
}
}
}
}
You need to use the underling method to determine accessibility, using PropertyInfo.GetGetMethod() or PropertyInfo.GetSetMethod().
// Get a PropertyInfo instance...
var info = typeof(string).GetProperty ("Length");
// Then use the get method or the set method to determine accessibility
var isPublic = (info.GetGetMethod(true) ?? info.GetSetMethod(true)).IsPublic;
Note, however, that the getter & setter may have different accessibilities, e.g.:
class Demo {
public string Foo {/* public/* get; protected set; }
}
So you can't assume that the getter and the setter will have the same visibility.

Categories

Resources