How to get generic method parameter property name in C#? [duplicate] - c#

This question already has answers here:
How can I get the name of a variable passed into a function?
(23 answers)
Is it possible to get VALUE from nameof(VALUE)?
(3 answers)
Closed 1 year ago.
The expected result is to get the method parameter T property Name. Here is my code,
I have tried few suggested workarounds to make use of class ABC typeof(ABC).GetProperties - didn't get the expected result.
public class ABC
{
public string Name { get; set; }
public int RecordCount { get; set; }
public decimal Total { get; set; }
public DateTime CreatedDate { get; set; }
}
public void ExecuteMain()
{
var item = new ABC
{
Name = "TestUser A",
RecordCount = 10,
Total = 100.20m,
CreatedDate = DateTime.Now
};
AddTest<string>(item.Name);
AddTest<int>(item.RecordCount);
AddTest<decimal>(item.Total);
AddTest<DateTime>(item.CreatedDate);
}
private string AddTest<T>(T field)
{
var resultName = nameof(field); // should return as "Name"
var resultValue = field.ToString(); // this returns "TestUser A" which is correct
//Record Count, Total, CreatedDate to add later
return $"Name = {resultName}:{resultValue}";
}
Expecting result in this line
var resultName = nameof(field); // should return as "Name"

I don't know why you are trying to do that, but this is how you should write it :
using System.Reflection;
public class ABC
{
public string Name { get; set; }
public int RecordCount { get; set; }
public decimal Total { get; set; }
public DateTime CreatedDate { get; set; }
}
public void ExecuteMain()
{
var item = new ABC
{
Name = "TestUser A",
RecordCount = 10,
Total = 100.20m,
CreatedDate = DateTime.Now
};
AddTest(item.GetType().GetProperty(nameof(item.Name)), item);
AddTest(item.GetType().GetProperty(nameof(item.RecordCount)), item);
AddTest(item.GetType().GetProperty(nameof(item.Total)), item);
AddTest(item.GetType().GetProperty(nameof(item.CreatedDate)), item);
}
private string AddTest(PropertyInfo prop, object o)
{
var resultName = prop.Name; // should return as "Name"
var resultValue = prop.GetValue(o); // this returns "TestUser A" which is correct
//Record Count, Total, CreatedDate to add later
return $"Name = {resultName}:{resultValue}";
}
However, Reflection is slow to run.
If it's just to have the name, you could do much simpler :
public void ExecuteMain()
{
var item = new ABC
{
Name = "TestUser A",
RecordCount = 10,
Total = 100.20m,
CreatedDate = DateTime.Now
};
AddTest(nameof(item.Name), item.Name);
AddTest(nameof(item.RecordCount), item.RecordCount);
AddTest(nameof(item.Total), item.Total);
AddTest(nameof(item.CreatedDate), item.CreatedDate);
}
private string AddTest(String resultName, object resultValue)
{
return $"Name = {resultName}:{resultValue}";
}

Related

Except using two List [duplicate]

This question already has answers here:
Using Linq Except not Working as I Thought
(5 answers)
Closed 21 days ago.
I have two list that I want to get the different. So because CustomerId 1 is in both I only want to return CustomerId 2. I am using Exceptbut I a returning CustomerId 1 and 2 any help would be great
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
List<Participants> Participants1 = new List<Participants>();
Participants vm1 = new Participants();
vm1.CustomerId = 1;
vm1.FirstName = "Bill";
vm1.LastName= "Jackson";
Participants1.Add(vm1);
List<Participants> Participants2 = new List<Participants>();
Participants vm2 = new Participants();
vm2.CustomerId = 2;
vm2.FirstName = "Steve";
vm2.LastName= "Jackson";
Participants vm3 = new Participants();
vm3.CustomerId = 1;
vm3.FirstName = "Bill";
vm3.LastName= "Jackson";
Participants2.Add(vm2);
Participants2.Add(vm3);
var inFirstOnly = Participants2.Except(Participants1);
foreach(Participants item in inFirstOnly){
Console.WriteLine(item.CustomerId);
}
}
public class Participants
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
To only return CustomerId 2, you need to implement a custom equality comparer to compare objects based on the CustomerId property instead of the default comparison which compares references.
public class ParticipantsComparer : IEqualityComparer<Participants>
{
public bool Equals(Participants x, Participants y)
{
return x.CustomerId == y.CustomerId;
}
public int GetHashCode(Participants obj)
{
return obj.CustomerId.GetHashCode();
}
}
Usage:
var inFirstOnly = Participants2.Except(Participants1, new ParticipantsComparer());

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

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

How adding items to list

I have model in my project. Here is code of model
public partial class Logging
{
public string Imei { get; set; }
public DateTime CurDateTime { get; set; }
public Nullable<System.DateTime> GPSDateTime2 { get; set; }
public Nullable<decimal> Latitude2 { get; set; }
public Nullable<decimal> Longitude2 { get; set; }
public string Speed { get; set; }
public Nullable<int> Datatype { get; set; }
public int Id { get; set; }
[NotMapped]
public TimeSpan? FirstStartDifference
{
get
{
if (CurDateTime != null)
{
var midnight = new DateTime(CurDateTime.Year, CurDateTime.Month, CurDateTime.Day, 00, 00, 00);
var difference = CurDateTime - midnight;
return difference;
}
return null;
}
}
[NotMapped]
public TimeSpan? LastStartDifference
{
get
{
if (CurDateTime != null)
{
var midnight = new DateTime(CurDateTime.Year, CurDateTime.Month, CurDateTime.Day, 23, 59, 00);
var difference = midnight - CurDateTime;
return difference;
}
return null;
}
}
[NotMapped]
public int coeff = 2;
}
I need to get some items from database , it's first entry, where Datatype==1 and Last where Datatype ==2.
So I write this method on back-end
public JsonResult GetStops()
{
using (var ctx = new GoogleMapTutorialEntities())
{
var firstitem = ctx.Loggings.Where(x => x.Datatype == 2).AsEnumerable().Select(
x => new
{
lng = x.Longitude2,
lat = x.Latitude2,
difference = (int)(x.FirstStartDifference?.TotalMinutes ?? -1) * x.coeff
}).FirstOrDefault();
var lastItem = ctx.Loggings.Where(x => x.Datatype == 2).AsEnumerable().Select(
x => new
{
lng = x.Longitude2,
lat = x.Latitude2,
difference = (int)(x.LastStartDifference?.TotalMinutes ?? -1) * x.coeff
}).LastOrDefault();
List<Logging> items = new List<Logging> {firstitem, lastItem};
return Json(firstitem, JsonRequestBehavior.AllowGet);
}
}
After this I need to add firstitem and lastitem to list.
I write it like this List<Logging> items = new List<Logging> {firstitem, lastItem};
But I get an error
Severity Code Description Project File Line Suppression State
Error CS1950 The best overloaded Add method 'List.Add(Logging)' for the collection initializer has some invalid arguments Heatmap C:\Users\nemes\source\repos\Heatmap\Heatmap\Controllers\HomeController.cs 37 Active
Severity Code Description Project File Line Suppression State
Error CS1503 Argument 1: cannot convert from '' to 'Heatmap.Models.Logging' Heatmap C:\Users\nemes\source\repos\Heatmap\Heatmap\Controllers\HomeController.cs 37 Active
for this List<Logging> items = new List<Logging> {firstitem, lastItem};
How I can add them to List?
You are returning an anonymous type instead of Logging. The firstitem and lastItem are Anonymous Types. Change your code to this:
x => new Logging
{
Longitude2 = x.Longitude2,
Latitude2 = x.Latitude2,
//And other properties
}
And if you still get error probably it is because you cannot project onto a mapped entity then you need to create a DTO class with needed properties from the Logging entity:
public class LoggingDTO
{
public string Longitude2 { get; set; }
public string Latitude2 { get; set; }
//And other properties
}
Then:
x => new LoggingDTO

return multiple reader.cast<>

All I want to do is return multiple reader.cast<> so that i can use 2 sqlcommands.
var first =reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names = x.GetString(0),
Values = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(1)
}).ToList();
reader.NextResult();
var second=reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names2 = x.GetString(0),
Values2 = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble2 = x.GetDouble(1)
}).ToList();
All I want to do is return var first and var second. Please help :(
I'm using this Location.cs for parameters:
namespace MVCRealtime
{
public class LocationInfo
{
public string Names { get; set; }
public string Values { get; set; }
public double ValuesDouble { get; set; }
public string Names2 { get; set; }
public string Values2 { get; set; }
public double ValuesDouble2 { get; set; }
}
}
public static class ReaderHelper
{
public static IEnumerable<TElem> GetData<TElem>(this IDataReader reader, Func<IDataRecord, TElem> buildObjectDelegat)
{
while (reader.Read())
{
yield return buildObjectDelegat(reader);
}
}
}
// ...
var result = reader.GetData(x => new LocationInfo()
{
Names = x.GetString(0),
Values = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(1)
}).Take(2);
So you get 1st var in 1st element of the result and 2nd var in 2nd element.

Categories

Resources