Set property in nested object using reflection - c#

I'm trying to set Address1 in obj1 using reflection and I can't figure out how to get a reference to the correct object. I'm not sure how to get a reference to the Address1 instance to pass into the first parameter of SetValue()
Round 1:
public class StackOverflowReflectionTest
{
[Fact]
public void SetDeepPropertyUsingReflection()
{
var breadCrumb = ".Addresses[0].Address1";
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var newAddress1 = "123 Second Street";
var propNames = breadCrumb.Split(".");
for (var index = 0; index < propNames.Length; index++)
{
var propName = propNames[index];
if (propName.Contains("["))
{
var propNameToGet = propName.Substring(0, propName.IndexOf("[", StringComparison.Ordinal));
var prop = obj1.GetType().GetProperty(propNameToGet);
var leftBrace = propName.IndexOf("[", StringComparison.Ordinal);
var rightBrace = propName.IndexOf("]", StringComparison.Ordinal);
var position = int.Parse(propName.Substring(leftBrace + 1, rightBrace - leftBrace - 1));
var propNameToSet = propNames[index + 1];
var propToSet = prop.PropertyType.GetGenericArguments()[position].GetProperty(propNameToSet);
propToSet.SetValue(obj1, newAddress1);
}
else
{
//TODO: deal with different types
}
}
}
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
}
Round 2 based on Ed's feedback, still stuck on how to get the value on this line: var value = property.GetValue(obj, new object[] { indexPart });
public class StackOverflowReflectionTest
{
[Fact]
public void SetDeepPropertyUsingReflectionRound2()
{
var breadCrumb = "Addresses[0].Address1";
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var newAddress1 = "123 Second Street";
SetPropertyValueByPath(obj1, breadCrumb, newAddress1);
}
public bool CrackPropertyName(string name, out string namePart, out object indexPart)
{
if (name.Contains("["))
{
namePart = name.Substring(0, name.IndexOf("[", StringComparison.Ordinal));
var leftBrace = name.IndexOf("[", StringComparison.Ordinal);
var rightBrace = name.IndexOf("]", StringComparison.Ordinal);
indexPart = name.Substring(leftBrace + 1, rightBrace - leftBrace - 1);
return true;
}
else
{
namePart = name;
indexPart = null;
return false;
}
}
public object GetPropertyValue(object obj, string name)
{
if(CrackPropertyName(name, out var namePart, out var indexPart))
{
var property = obj.GetType().GetProperty(namePart);
var value = property.GetValue(obj, new object[] { indexPart });
return value;
}
else
{
return obj.GetType().GetProperty(name);
}
}
public void SetPropertyValue(object obj, string name, object newValue)
{
var property = typeof(Address).GetProperty(name);
property.SetValue(obj, newValue);
}
public void SetPropertyValueByPath(object obj, string path, object newValue)
{
var pathSegments = path.Split(".");
if (pathSegments.Length == 1)
{
SetPropertyValue(obj, pathSegments[0], newValue);
}
else
{
//// If more than one remaining segment, recurse
var child = GetPropertyValue(obj, pathSegments[0]);
SetPropertyValueByPath(child, String.Join(".", pathSegments.Skip(1)), newValue);
}
}
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
}
Solution:
public class StackOverflowReflectionTest
{
[Fact]
public void SetDeepPropertyUsingReflectionSolution()
{
var breadCrumb = "Addresses[0].Address1";
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var newAddress1 = "123 Second Street";
SetPropertyValueByPath(obj1, breadCrumb, newAddress1);
}
public bool CrackPropertyName(string name, out string namePart, out object indexPart)
{
if (name.Contains("["))
{
namePart = name.Substring(0, name.IndexOf("[", StringComparison.Ordinal));
var leftBrace = name.IndexOf("[", StringComparison.Ordinal);
var rightBrace = name.IndexOf("]", StringComparison.Ordinal);
indexPart = name.Substring(leftBrace + 1, rightBrace - leftBrace - 1);
return true;
}
else
{
namePart = name;
indexPart = null;
return false;
}
}
public object GetPropertyValue(object obj, string name)
{
if(CrackPropertyName(name, out var namePart, out var indexPart))
{
var property = obj.GetType().GetProperty(namePart);
var list = property.GetValue(obj);
var value = list.GetType().GetProperty("Item").GetValue(list, new object[] { int.Parse(indexPart.ToString()) });
return value;
}
else
{
return obj.GetType().GetProperty(namePart);
}
}
public void SetPropertyValue(object obj, string name, object newValue)
{
var property = typeof(Address).GetProperty(name);
property.SetValue(obj, newValue);
}
public void SetPropertyValueByPath(object obj, string path, object newValue)
{
var pathSegments = path.Split(".");
if (pathSegments.Length == 1)
{
SetPropertyValue(obj, pathSegments[0], newValue);
}
else
{
//// If more than one remaining segment, recurse
var child = GetPropertyValue(obj, pathSegments[0]);
SetPropertyValueByPath(child, String.Join(".", pathSegments.Skip(1)), newValue);
}
}
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}
}

Type.GetGenericArguments() doesn't do anything like what I think you're assuming.
What you want here is recursion. Given ”Foo.Bar[1].Baz”, get Foo. Get Bar[1] from that. Get the PropertyInfo from Baz from its parent, use that to set the value of the Baz property of the Bar[1] property of Foo.
To break it down:
Write a method that "cracks" a property name and uses out parameters to return both the name part and the index value part: "IndexedProperty[1]" goes in; "IndexedProperty" and integer 1 come out. "FooBar" goes in, "FooBar" and null come out. It returns true if there's an indexer, false if not.
bool CrackPropertyName(string name, out string namePart, out object indexPart)
Write a method that takes an object, and a string "PropertyName" or "IndexedPropety[0]" (not a path -- no dot) and returns the value of that property on that object. It uses CrackPropertyName() to simplify its job.
object GetPropertyValue(object obj, string name)
Write a method that sets a property value by name (not by path, just by name). Again, it uses CrackPropertyName() to simplify its job.
void SetPropertyValue(object obj, string name, object newValue)
A recursive method using the above:
void SetPropertyValueByPath(object obj, string path, object newvalue)
{
var pathSegments = /* split path on '.' */;
if (pathSegments.Length == 1)
{
SetPropertyValue(obj, pathSegments[0], newValue);
}
else
{
// If more than one remaining segment, recurse
var child = GetNamedPropertyvalue(obj, pathSegments[0]);
return SetPropertyValueByPath(obj, String.Join(".", pathSegments.Skip(1)), newValue);
}
}
These methods are all pretty trivial. Since you're using reflection anyway, you may as well go whole hog and write one non-generic method that sets any property of anything.

Here is the quick and dirty I put together in LINQPad that changes the Address1 property of the object you have defined.
void Main()
{
var obj1 = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var index = 0;
var addressList = typeof(Person)
.GetProperty("Addresses")
.GetValue(obj1);
var address = addressList.GetType()
.GetProperty("Item")
.GetValue(addressList, new object[]{index});
address.GetType()
.GetProperty("Address1")
.SetValue(address,"321 Fake Street");
Console.WriteLine(obj1.Addresses[index].Address1); // Outputs 321 Fake Street
}
// Define other methods and classes here
public class Person
{
public string Name { get; set; }
public IList<Address> Addresses { get; set; }
}
public class Address
{
public string Address1 { get; set; }
}

If you want to use reflection to get the value of the Addresses property from an instance of Person you would do this:
var myPerson = new Person()
{
Name = "Eric",
Addresses = new List<Address>()
{
new Address() {Address1 = "123 First Street"}
}
};
var property = typeof(Person).GetProperty("Addresses");
var addresses = (IList<Address>) property.GetValue(myPerson );
First you're finding the property - an instance of PropertyInfo - which belongs to the Person type. Then you're retrieving the value of that property for a specific instance of Person, myPerson.
addresses is an IList<Address> so there's not much use in using reflection to get a particular Address from the list. But if for some reason you wanted to:
private Address GetAddressAtIndex(IList<Address> addresses, int index)
{
var property = typeof(IList<Address>).GetProperty("Item");
var address = (Address) property.GetValue(addresses, new object []{index});
return address;
}
This is essentially the same as the first example, except that in this case the property (Item) requires an index. So we use the overload of GetValue that accepts one or more indexes.
Now you've got an instance of Address. I'm doing each of these in separate steps because they are all separate steps. There's no one step that will perform the entire operation.
If you have an instance of an address and you want to use reflection to set the Address1 property:
private void SetAddress1OnAddress(Address address, string address1Value)
{
var property = typeof(Address).GetProperty("Address1");
property.SetValue(address, address1Value);
}
Very similar. You're first retrieving the Address1 property and then calling its SetValue method to set the value on a specific instance if Address.

Related

C# how to recursively search object for objects of type x [duplicate]

This question already has answers here:
Recursively Get Properties & Child Properties Of A Class
(5 answers)
Closed 2 years ago.
I need to retrieve all objects of type X from a complex object.
public class Dog
{
public string Name {get;set;}
public int Age {get;set;}
public Owner Owner{get;set;}
public Kennel Kennel {get;set;}
}
public class Kennel
{
public string Name {get;set;}
public Address Address{get;set;}
public List<Dog> Dogs {get;set;}
}
public class Owner
{
public string Name {get;set;}
public string State {get;set;}
public Address Address{get;set;}
}
If I have an object composed of Dog, I would need to retrieve all address objects in all child objects.
If I understand you correctly, you might need to use Reflection to retrieve the Address objects:
public static class MyExtensions
{
public static List<Address> GetAddresses(this Dog dog)
{
var addresses = new List<Address>();
GetAddress(dog, ref addresses);
return addresses;
}
private static void GetAddress(object property, ref List<Address> addresses)
{
if (property == null)
{
return;
}
var props = property.GetType()
.GetProperties()
.Where(p => p.PropertyType.IsClass &&
!p.PropertyType.IsPrimitive)
.ToList();
foreach (var prop in props)
{
if (prop.PropertyType == typeof(Address))
{
var address = prop.GetValue(property) as Address;
addresses?.Add(address);
}
var next = prop.GetValue(obj: property);
// When property is a collection
if (next is IEnumerable collection)
{
foreach (var item in collection)
{
GetAddress(property: item,
addresses: ref addresses);
}
}
else
{
GetAddress(property: next,
addresses: ref addresses);
}
}
}
}
And usage example:
var dogs = new List<Dog>();
for (int i = 0; i < 5; i++)
{
var dog = new Dog
{
Name = $"Dog - {i}",
Age = 10,
Kennel = new Kennel
{
Address = new Address()
},
Kennels = new List<Kennel>
{
new Kennel
{
Address = new Address()
},
new Kennel
{
Address = new Address()
},
new Kennel()
{
Address = new Address()
}
},
Owner = new Owner
{
Address = new Address(),
Kennel = new Kennel
{
Address = new Address()
}
}
};
dogs.Add(dog);
}
var address = dogs.Select(dog => dog.GetAddresses());
Console.WriteLine(address.Sum(a => a.Count)); // will print 30
Please note that using Reflection could affect the performance of your application.
You don't need recursion here. Just SelectMany
Kennel home = getKennel();
List<Address> AllOwnerAddresses = home.Dogs.SelectMany(d = > d.Owner.Address).ToList();
Assuming that the structure is a tree
static IEnumerable<Address> GetAddresses(Dog dog)
{
yield return dog.Owner.Address;
yield return dog.Kennel.Address;
foreach (var addr in dog.Kennel.Dogs.SelectMany(dogC => GetAddresses(dogC)))
{
yield return addr;
}
}

Email Generator iteration through a list

I've written a function that generates an HTML email and fills it with information from a database.
I've been trying to iterate through a list, but can't seem to get the function to be generic and run throught the Items list.
Here is the Email Generator function. It is fairly generic, so that it can be used in a wide variety of email templates.
public interface IMailObject
{
string Subject { get; }
}
public interface IEmailGenerator
{
MailMessage generateEmail(IMailObject mailObject, string htmlTemplate, string textTemplate);
}
public class EmailGenerator : IEmailGenerator, IRegisterInIoC
{
private string mergeTemplate(string template, object obj)
{
Regex operationParser = new Regex(#"\$(?:(?<operation>[\w\-\,\.]+)\x20)(?<value>[\w\-\,\.]+)\$", RegexOptions.Compiled);
Regex valueParser = new Regex(#"\$(?<value>[\w\-\,\.]+)\$", RegexOptions.Compiled);
var operationMatches = operationParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in operationMatches)
{
string operation = match.Groups["operation"].Value;
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
if (propertyInfo == null)
throw new TillitException(String.Format("Could not find '{0}' in object of type '{1}'.", value, obj));
object dataValue = propertyInfo.GetValue(obj, null);
if (operation == "endforeach")
{
string foreachToken = "$foreach " + value + "$";
var startIndex = template.LastIndexOf(foreachToken, match.Index);
var templateBlock = template.Substring(startIndex + foreachToken.Length, match.Index - startIndex - foreachToken.Length);
var items = (IEnumerable) value;
string blockResult = "";
foreach (object item in items)
{
blockResult += mergeTemplate(templateBlock, item);
}
template = template.Remove(startIndex, match.Index - startIndex).Insert(startIndex, blockResult);
}
}
var valueMatches = valueParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in valueMatches)
{
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
if (propertyInfo == null)
throw new Exception(String.Format("Could not find '{0}' in object of type '{1}'.", value, obj));
object dataValue = propertyInfo.GetValue(obj, null);
template = template.Remove(match.Index, match.Length).Insert(match.Index, dataValue.ToString());
}
return template;
}
public MailMessage generateEmail(IMailObject mailObject, string htmlTemplate, string textTemplate)
{
var mailMessage = new MailMessage();
mailMessage.IsBodyHtml = true;
mailMessage.Subject = mailObject.Subject;
mailMessage.BodyEncoding = Encoding.UTF8;
// Create the Plain Text version of the email
mailMessage.Body = this.mergeTemplate(textTemplate, mailObject);
// Create the HTML version of the email
ContentType mimeType = new System.Net.Mime.ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(this.mergeTemplate(htmlTemplate, mailObject), mimeType);
mailMessage.AlternateViews.Add(alternate);
return mailMessage;
}
}
And here is a case of the message data:
public class MessageData : IMailObject
{
public string Property1 { get; private set; }
public string Property2 { get; private set; }
public string Property3 { get; private set; }
public string Property4 { get; private set; }
public string Property5 { get; private set; }
public string Property6 { get; private set; }
public string Subject
{
get { return this.Property1 + DateTime.Now.ToShortDateString(); }
}
public List<MessageItemData> Items { get; private set; }
public MessageData(string property1, string property2, string property3, DateTime property4, string property7, string property8, DateTime property9, DateTime property10, int property11, double property12, string property5, string property6)
{
this.Property1 = property1;
this.Property2 = property2;
this.Property3 = property3;
this.Property4 = property4.ToShortDateString();
this.Property5 = property5;
this.Property6 = property6;
this.Items = new List<MessageItemData>();
this.Items.Add(new MessageItemData(property7, property8, property9, property10, property11, property12));
}
}
public class MessageItemData
{
public string Property7 { get; private set; }
public string Property8 { get; private set; }
public string Property9 { get; private set; }
public string Property10 { get; private set; }
public int Property11 { get; private set; }
public double Property12 { get; private set; }
public MessageItemData( string property7, string property8, DateTime property9, DateTime property10, int property11, double property12)
{
this.Property7 = property7;
this.Property8 = property8;
this.Property9 = property9.ToShortDateString();
this.Property10 = property10.ToShortDateString();
this.Property11 = property11;
this.Property12 = property12;
}
}
The function works when there is only one set of elements being used. If we use the MessageData class as an example. All the information will be replaced correctly, but I'm wanting to improve the email generator function, because this particular MessageData class has a list of objects, where Property7 to Property12 will be replaced multiple times.
The function is started at: if (operation == "endforeach"), but I need some help to improve it so that it runs through the: var items = (IEnumerable) value;, so that the function returns TemplateHeader + TemplateItem + TemplateItem + ...however many TemplateItems there are + TemplateFooter. It currently will only return TemplateHeader + TemplateItem + TemplateFooter, even though there are multiple items in the list, it will only return the first item.
In this case I'm assuming I need to get the List Items. I've been trying to implement it into the EmailGenerator just below:
var items = (IEnumerable) value;
with the code:
if (propertyInfo.PropertyType == typeof(List<>))
{
foreach (var item in items)
{
Console.WriteLine(item);
}
}
Console.WriteLine is just for testing purposes, to see if I get any values in Debug(which I'm currently getting null)
Assuming the type of the Items property is the same across all instances you may want to try using IsInstanceOfType instead. And then get the value of the property via the GetValue method. Reflection can be confusing at times ;) but hopefully, it is what you are looking for.
var data = new MessageData("a", "b", "c", DateTime.Now, "d", "e", DateTime.Now, DateTime.Now, 1, 2, "f", "g");
data.Items.Add(new MessageItemData("7", "8", DateTime.Now, DateTime.Now, 11, 12));
data.Items.Add(new MessageItemData("71", "81", DateTime.Now, DateTime.Now, 111, 112));
var dataType = data.GetType();
foreach (var propertyInfo in dataType.GetProperties())
{
if (propertyInfo.PropertyType.IsInstanceOfType(data.Items))
{
foreach (var item in (List<MessageItemData>)propertyInfo.GetValue(data))
{
Console.WriteLine(item);
}
}
}

Set Object null if all its properties are null in C#

I want to write a function which it turns every properies and child properties of an object. And if all properties of one property are null, then I will set that property as null. I will explain with one example.
For example, if both TeacherName and TeacherSurname are null then I want to set Teacher to null. Then, if ExamMark and ExamName and Teacher are null, then Exam will be null.
You can see json version of like my question from this link Json Version
public class Student
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
public Exam Exam { get; set; }
}
public class Exam
{
public string ExamMark { get; set; }
public string ExamName { get; set; }
public Teacher Teacher { get; set; }
}
public class Teacher
{
public string TeacherName { get; set; }
public string TeacherSurname { get; set; }
}
public class Address
{
public string Country { get; set; }
public string City { get; set; }
}
I wrote this method. But this doing only first step. But I need recursive for child class. How can I convert this method to recursive?
public static object ConvertToNull(object obj)
{
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
var mainClassProperties = properties.Where(p => p.PropertyType.Assembly == objType.Assembly);
foreach (var mainClassProperty in mainClassProperties)
{
object propValue = mainClassProperty.GetValue(obj, null);
var classAllProperties = propValue.GetType().GetProperties();
if (propValue.GetType().GetProperties().All(propertyInfo => propertyInfo.GetValue(propValue) == null))
{
mainClassProperty.SetValue(obj, null);
}
}
return obj;
}
What you're asking for is actually an anti-pattern. What this does is it propagates the requirement for tons of null checks in your code. Not only do you have to check if all properties are null, you also have to check if your object is null when you want to use it. Also reflection is very slow and if you're doing this often, you'll bog down your application.
You should look into the Null Object Pattern. It basically does what you want it to, and you remove all those pesky null checks:
public class Student
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
public Exam Exam { get; set; }
public static Student NullStudent { get; } = new Student
{
Name = null,
Surname = null,
Address = Address.NullAddress,
Exam = Exam.NullExam,
}
}
You can do this for each object in your code that acts this way (you can see that I did it on your nested Address and Exam types as well) and then instead of doing this:
if (Student.EverythingIsNull) { Student = null }
if (Student is null) { //do null stuff }
You can do this:
if (item == Student.NullStudent) { //do null stuff }
This leads to clearer code, your intent stands out more, and you can specifically define in each object what constitutes as null.
using Generics and Reflection.
public T ConvertToNull<T>(T model) where T : class
{
if (model == null) return null;
Type type = model.GetType();
PropertyInfo[] properties = type.GetProperties();
var valueTypes = properties.Where(p => p.PropertyType.Assembly != type.Assembly);
var nonValueTypes = properties.Where(p => p.PropertyType.Assembly == type.Assembly);
foreach (var nonValueType in nonValueTypes)
nonValueType.SetValue(model, ConvertToNull(nonValueType.GetValue(model)));
if (valueTypes.All(z => z.GetValue(model) == null) && nonValueTypes.All(z => z.GetValue(model) == null))
return null;
else
return model;
}
Here you can call it
List<Student> students = new List<Student>();
Student student = new Student() { Name = "StudentName", Surname = "StudentSurname", Address = new Address() { City = "City", Country = "Country" }, Exam = new Exam() { ExamMark = "ExamMark", ExamName = "ExamName", Teacher = new Teacher() { TeacherName = "TeacherName", TeacherSurname = "TeacherSurname" } } };
Student student2 = new Student() { Name = "StudentName", Surname = "StudentSurname", Address = new Address(), Exam = new Exam() { ExamMark = "ExamMark", ExamName = "ExamName", Teacher = new Teacher() } };
students.Add(student);
students.Add(student2);
List<Student> results = new List<Student>();
foreach (var item in students)
{
var result = ConvertToNull(item);
results.Add(result);
}
Have a look at this The GetValueOrNull should work and do what you need, not tested with all possible use cases but it oculd be tweaked a little if doesn't work in all cases
public static bool IsSimpleType(Type type)
{
return
type.IsPrimitive ||
new Type[] {
typeof(Enum),
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
;
}
public object GetValueOrNull(object obj)
{
if (obj == null) return null;
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
var simpleTypes = properties.Where(t => IsSimpleType(t.PropertyType));
var nonValueTypes = properties.Where(p => !simpleTypes.Contains(p));
foreach (var child in nonValueTypes)
{
child.SetValue(obj, GetValueOrNull(child.GetValue(obj)));
}
return simpleTypes.All(z => z.GetValue(obj) == null) && nonValueTypes.All(z => z.GetValue(obj) == null) ? null : obj;
}
call it as follows
var student = new Student { Address = new Address { }, Exam = new Exam { Teacher = new Teacher() } };
var test = GetValueOrNull(student);
hope it helps :)

Dynamically build an object from a strongly typed class using C#?

Currently, am adding the properties and values to the object manually like this example and sending to Dapper.SimpleCRUD to fetch data from Dapper Orm. This is the desired output I would like to achieve.
object whereCriteria = null;
whereCriteria = new
{
CountryId = 2,
CountryName = "Anywhere on Earth",
CountryCode = "AOE",
IsActive = true
};
The following class should build the object in the above mentioned format and return the ready-made object.
public static class WhereClauseBuilder
{
public static object BuildWhereClause(object model)
{
object whereObject = null;
var properties = GetProperties(model);
foreach (var property in properties)
{
var value = GetValue(property, model);
//Want to whereObject according to the property and value. Need help in this part!!!
}
return whereObject;
}
private static object GetValue(PropertyInfo property, object model)
{
return property.GetValue(model);
}
private static IEnumerable<PropertyInfo> GetProperties(object model)
{
return model.GetType().GetProperties();
}
}
This function WhereClauseBuilder.BuildWhereClause(object model) should return the object in expected format (mentiond above). Here is the implementation of how I would like to use.
public sealed class CountryModel
{
public int CountryId { get; set; }
public string CountryName { get; set; }
public string CountryCode { get; set; }
public bool IsActive { get; set; }
}
public class WhereClauseClass
{
public WhereClauseClass()
{
var model = new CountryModel()
{
CountryCode = "AOE",
CountryId = 2,
CountryName = "Anywhere on Earth",
IsActive = true
};
//Currently, won't return the correct object because the implementation is missing.
var whereClauseObject = WhereClauseBuilder.BuildWhereClause(model);
}
}
Maybe something like that:
private const string CodeTemplate = #"
namespace XXXX
{
public class Surrogate
{
##code##
}
}";
public static Type CreateSurrogate(IEnumerable<PropertyInfo> properties)
{
var compiler = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters { GenerateInMemory = true };
foreach (var item in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic))
{
compilerParameters.ReferencedAssemblies.Add(item.Location);
}
var propertiesCode =
string.join("\n\n", from pi in properties
select "public " + pi.PropertyType.Name + " " + pi.Name + " { get; set; }");
var source = CodeTemplate.Replace("##code##", propertiesCode);
var compilerResult = compiler.CompileAssemblyFromSource(compilerParameters, source);
if (compilerResult.Errors.HasErrors)
{
throw new InvalidOperationException(string.Format("Surrogate compilation error: {0}", string.Join("\n", compilerResult.Errors.Cast<CompilerError>())));
}
return compilerResult.CompiledAssembly.GetTypes().First(x => x.Name == "Surrogate");
}
And now use it:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var surrogateType = CreateSurrogate(properties);
var result = Activator.CreateInstance(surrogateType);
foreach (var property in properties)
{
var value = GetValue(property, model);
var targetProperty = surrogateType.GetProperty(property.Name);
targetProperty.SetValue(result, value, null);
}
return result;
}
I didn't compile that. It's only written here. Maybe there are some errors. :-)
EDIT:
To use ExpandoObject you can try this:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var result = (IDictionary<string, object>)new ExpandoObject();
foreach (var property in properties)
{
var value = GetValue(property, model);
result.Add(property.Name, value);
}
return result;
}
But I don't know whether this will work for you.

Creating all objects using reflection of an object properties!

Okay here is the scenario! I have a Person object which has an Address object. Person has a list of Addresses.
Now, I want to iterate through the properties of the Person and when I reach a List I want to create an object of the Address object. How can i do that?
Update:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
private List<Address> _addresses = new List<Address>();
public void AddAddress(Address address)
{
_addresses.Add(address);
address.Person = this;
}
public List<Address> Addresses
{
get { return _addresses; }
set { _addresses = value; }
}
}
var properties = item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(var property in properties)
{
var propertyType = property.PropertyType;
if (!propertyType.IsGenericType) continue;
var obj = Activator.CreateInstance(propertyType);
var genericType = obj.GetType().GetGenericTypeDefinition();
Console.WriteLine(obj.GetType().Name);
var type = property.GetType();
}
The above reflection code returns me a List but it is of type List. I want the Generic Type which is address.
Tony, if you have access to Address class then you could simply do this.
var properties = item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
List<Address> retVal;
foreach (var property in properties)
{
var propertyType = property.PropertyType;
if (!propertyType.IsGenericType) continue;
retVal = property.GetValue(item, new object[] { }) as List<Address>;
if (retVal != null)
break;
}
//now you have your List<Address> in retVal

Categories

Resources