Email Generator iteration through a list - c#

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);
}
}
}

Related

Map one class data to another class with iteration

I have a C# project and looking for simple solution for map one class object data to list of another class object.
This is my input class
public class RatesInput
{
public string Type1 { get; set; }
public string Break1 { get; set; }
public string Basic1 { get; set; }
public string Rate1 { get; set; }
public string Type2 { get; set; }
public string Break2 { get; set; }
public string Basic2 { get; set; }
public string Rate2 { get; set; }
public string Type3 { get; set; }
public string Break3 { get; set; }
public string Basic3 { get; set; }
public string Rate3 { get; set; }
}
This is my another class structure
public class RateDetail
{
public string RateType { get; set; }
public decimal Break { get; set; }
public decimal Basic { get; set; }
public decimal Rate { get; set; }
}
it has a object like below. (For easiering the understanding, I use hardcoded values and actually values assign from a csv file)
RatesInput objInput = new RatesInput();
objInput.Type1 = "T";
objInput.Break1 = 100;
objInput.Basic1 = 50;
objInput.Rate1 = 0.08;
objInput.Type2 = "T";
objInput.Break2 = 200;
objInput.Basic2 = 50;
objInput.Rate2 = 0.07;
objInput.Type3 = "T";
objInput.Break3 = 500;
objInput.Basic3 = 50;
objInput.Rate3 = 0.06;
Then I need to assign values to "RateDetail" list object like below.
List<RateDetail> lstDetails = new List<RateDetail>();
//START Looping using foreach or any looping mechanism
RateDetail obj = new RateDetail();
obj.RateType = //first iteration this should be assigned objInput.Type1, 2nd iteration objInput.Type2 etc....
obj.Break = //first iteration this should be assigned objInput.Break1 , 2nd iteration objInput.Break2 etc....
obj.Basic = //first iteration this should be assigned objInput.Basic1 , 2nd iteration objInput.Basic2 etc....
obj.Rate = //first iteration this should be assigned objInput.Rate1, 2nd iteration objInput.Rate2 etc....
lstDetails.Add(obj); //Add obj to the list
//END looping
Is there any way to convert "RatesInput" class data to "RateDetail" class like above method in C#? If yes, how to iterate data set?
Try this:
public class RatesList : IEnumerable<RateDetail>
{
public RatesList(IEnumerable<RatesInput> ratesInputList)
{
RatesInputList = ratesInputList;
}
private readonly IEnumerable<RatesInput> RatesInputList;
public IEnumerator<RateDetail> GetEnumerator()
{
foreach (var ratesInput in RatesInputList)
{
yield return new RateDetail
{
RateType = ratesInput.Type1,
Break = Convert.ToDecimal(ratesInput.Break1, new CultureInfo("en-US")),
Basic = Convert.ToDecimal(ratesInput.Basic1, new CultureInfo("en-US")),
Rate = Convert.ToDecimal(ratesInput.Rate1, new CultureInfo("en-US"))
};
yield return new RateDetail
{
RateType = ratesInput.Type2,
Break = Convert.ToDecimal(ratesInput.Break2),
Basic = Convert.ToDecimal(ratesInput.Basic2),
Rate = Convert.ToDecimal(ratesInput.Rate2, new CultureInfo("en-US"))
};
yield return new RateDetail
{
RateType = ratesInput.Type3,
Break = Convert.ToDecimal(ratesInput.Break3),
Basic = Convert.ToDecimal(ratesInput.Basic3),
Rate = Convert.ToDecimal(ratesInput.Rate3, new CultureInfo("en-US"))
};
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
And use:
var list = new RatesList(new List<RatesInput>() { objInput });
foreach (var item in list)
{
Console.WriteLine(item.Basic);
}
You can use Reflection to get the properties info like this:
var props = objInput.GetType().GetProperties();
var types = props.Where(x => x.Name.StartsWith("Type"))
.Select(x => x.GetValue(objInput)).ToList();
var breaks = props.Where(x => x.Name.StartsWith("Break"))
.Select(x => x.GetValue(objInput)).ToList();
var basics = props.Where(x => x.Name.StartsWith("Basic"))
.Select(x => x.GetValue(objInput)).ToList();
var rates = props.Where(x => x.Name.StartsWith("Rate"))
.Select(x => x.GetValue(objInput)).ToList();
List<RateDetail> lstDetails = new List<RateDetail>();
for (int i = 0; i < types.Count; i++)
{
lstDetails.Add(new RateDetail
{
RateType = types[i].ToString(),
Break = Convert.ToDecimal(breaks[i]),
Basic = Convert.ToDecimal(basics[i]),
Rate = Convert.ToDecimal(rates[i])
});
}

Set property in nested object using reflection

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.

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.

Create distinct expando object list

I have created a method which will create dynamic object list from an object list according to property list. In this case I have completed such task using Expandoobject. But I have failed to create distinct list of such expando object list. Please visit the following fidder and see my code.
public class Program
{
public static void Main()
{
var _dynamicObjectList = new List<Student>();
for (int i = 0; i < 5; i++)
{
_dynamicObjectList.Add(new Student { ID = i, Name = "stu" + i, Address = "address" + i, AdmissionDate = DateTime.Now.AddDays(i) , Age=15, FatherName="Jamal"+i, MotherName = "Jamila"+i});
}
//create again for checking distinct list
for (int i = 0; i < 5; i++)
{
_dynamicObjectList.Add(new Student { ID = i, Name = "stu" + i, Address = "address" + i, AdmissionDate = DateTime.Now.AddDays(i), Age = 15, FatherName = "Jamal" + i, MotherName = "Jamila" + i });
}
// var returnList = test2.GetDdlData<Object>(_dynamicObjectList, "ID,Name,Address");
// var returnList = test2.GetDdlData<Object>(_dynamicObjectList, "ID,FatherName,Address");
var returnList = test2.GetDdlData<Object>(_dynamicObjectList, "ID,Name,FatherName,MotherName,Age");
string strSerializeData = JsonConvert.SerializeObject(returnList);
Console.WriteLine(strSerializeData);
Console.ReadLine();
}
}
public class Student
{
public int? ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime AdmissionDate { get; set; }
public string FatherName { get; set; }
public string MotherName { get; set; }
public int Age { get; set; }
}
public static class test2
{
public static IList GetDdlData<T>(this IEnumerable<T> source, string userParams)
{
try
{
List<string> otherProperties = userParams.Split(',').ToList();
Dictionary<string, PropertyInfo> parentPropertyInfo = new Dictionary<string, PropertyInfo>();
Dictionary<string, Type> parentType = new Dictionary<string, Type>();
var dynamicObjectList = (from k in source select k).ToList();
if (dynamicObjectList.Count() > 0)
{
//if parentField exists then system will handle parent property
if (otherProperties.Count > 0)
{
foreach (string otherProperty in otherProperties)
{
//get parent field property info
PropertyInfo _info = dynamicObjectList[0].GetType().GetProperty(otherProperty);
parentPropertyInfo.Add(otherProperty, _info);
//get parent field propertyType
Type pType = Nullable.GetUnderlyingType(_info.PropertyType) ?? _info.PropertyType;
parentType.Add(otherProperty, pType);
}
}
}
//return List
IList<object> objList = new List<object>();
foreach (T obj in source)
{
var dynamicObj = new ExpandoObject() as IDictionary<string, Object>;
foreach (string otherProperty in otherProperties)
{
PropertyInfo objPropertyInfo = parentPropertyInfo.FirstOrDefault(m => m.Key == otherProperty).Value;
Type objPropertyType = parentType.FirstOrDefault(m => m.Key == otherProperty).Value;
Object data = (objPropertyInfo.GetValue(obj, null) == null) ? null : Convert.ChangeType(objPropertyInfo.GetValue(obj, null), objPropertyType, null);
dynamicObj.Add(otherProperty, data);
}
objList.Add(dynamicObj);
}
var returnUniqList = objList.Distinct().ToList();
return returnUniqList;
}
catch (Exception ex)
{
throw ex;
}
}
}
https://dotnetfiddle.net/hCuJwD
Just add the following code in the code block.
foreach(var objTemp in objList) {
bool isNotSimilar = true;
foreach(string property in otherProperties) {
//get sending object property data
object tempFValue = (objTemp as IDictionary < string, Object > )[property];
//get current object property data
object tempCValue = (dynamicObj as IDictionary < string, Object > )[property];
if (!tempFValue.Equals(tempCValue)) {
isNotSimilar = false;
break;
}
}
if (isNotSimilar) {
isDuplicate = true;
break;
}
}
DOTNETFIDDLE

Converting an array to object

I have 2 types of string: Mer and Spl
// Example
string testMer = "321|READY|MER";
string testSpl = "321|READY|SPL";
Then I will split them:
var splitMer = testMer.Split('|');
var splitSpl = testSpl.Split('|');
I have an object to save them
public class TestObject
{
public int id { get; set; }
public string status { get; set; }
public string type { get; set; }
}
Question: How to convert the Array into the TestObject?
var converted = new TestObject
{
id = int.Parse(splitMer[0]),
status = splitMer[1],
type = splitMer[2]
};
You will need to add some error checking.
var values = new List<string> { "321|READY|MER", "321|READY|SPL" };
var result = values.Select(x =>
{
var parts = x.Split(new [] {'|' },StringSplitOptions.RemoveEmptyEntries);
return new TestObject
{
id = Convert.ToInt32(parts[0]),
status = parts[1],
type = parts[2]
};
}).ToArray();
You just need to use object initializers and set your properties.By the way instead of storing each value into seperate variables, use a List.Then you can get your result with LINQ easily.
var splitMer = testMer.Split('|');
var testObj = new TestObject();
testObj.Id = Int32.Parse(splitMer[0]);
testObj.Status = splitMer[1];
testObj.type = splitMer[2];
How about adding a Constructor to your Class that takes a String as a Parameter. Something like this.
public class TestObject
{
public int id { get; set; }
public string status { get; set; }
public string type { get; set; }
public TestObject(string value)
{
var valueSplit = value.Split('|');
id = int.Parse(valueSplit[0]);
status = valueSplit[1];
type = valueSplit[2];
}
}
Usage:
TestObject tst1 = new TestObject(testMer);
TestObject tst2 = new TestObject(testSpl);

Categories

Resources