I have a class as below
public class Details
{
public string CreatedAt {set;get;)
public Order Order { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
public CustomerAddress Address { get; set; }
}
public class CustomerAddress
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
}
and I have HTML file with HTML content and few placeholder. I am replacing placeholders as below.
public static string ReplaceStringPlaceHolders(User Details)
{
string MyHTML= File.ReadAllText(#"...Path");
//Replacing one by one
string newstring= MyHTML.
.Replace("{created_at}", Details.CreatedAt)
.Replace("{customer_info.address.line_1}", Details.Customer.Address.Line1)
.Replace("{customer_info.address.line_2}", Details.Customer.Address.Line2)
.Replace("{customer_info.address.city}", Details.Customer.Address.City)
.Replace("{customer_info.address.state}", Details.Customer.Address.State)
.Replace("{customer_info.address.postal_code}", Details.Customer.Address.PostalCode)
.Replace("{customer_info.address.country}", Details.Customer.Address.Country)
return newstring;
}
but I don't like this way as I have put 50+ placeholders in my HTML file.
Is there a way that we can replace the placeholder when the placeholder name matches to class properties.
I am looking for something like this if possible:
MyHTML.replaceifPlaceHolderMatchesWithClassProperties(Label);
Kindly suggest.
Yes, you can read properties with a help of Reflection and Linq:
using System.Linq;
using System.Reflection;
....
private static string TryReadReflectionValue(object data, string name) {
if (name == null)
return null;
foreach (string item in name.Replace("_", "").Trim('{', '}').Split('.')) {
if (data == null)
return null;
var prop = data
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public)
.Where(p => p.Name.Equals(item, StringComparison.OrdinalIgnoreCase))
.Where(p => p.CanRead)
.Where(p => p.GetIndexParameters().Length <= 0)
.FirstOrDefault();
if (prop == null)
return null;
data = prop.GetValue(prop.GetGetMethod().IsStatic ? null : data);
}
return data?.ToString();
}
And match placeholders with a help of regular expressions:
using System.Text.RegularExpressions;
...
private static string MyReplace(string text, object data) {
if (text == null)
return text;
return Regex.Replace(
text,
#"\{\w._]+\}",
match => TryReadReflectionValue(data, match.Value) ?? match.Value);
}
Usage:
public static string ReplaceStringPlaceHolders(User Details) {
string MyHTML= File.ReadAllText(#"...Path");
return MyReplace(text, Details);
}
Here I assumed that
We always drop _ in placeholders, e.g {created_at} corresponds to CreatedAt property
We ignore case in placeholders: {created_at} == {Created_At} == {CREATED_at} etc.
All placeholders are in {letters,digits,_,.} format
i think this will help you:
string MyHTML = "{PersonId}, {Name}, {Family}, {Age}";
Person p = new Person();
p.PersonId = 0;
p.Name = "hello";
p.Family = "world";
p.Age = 15;
var properties = typeof(Person).GetProperties().ToDictionary(x => x, x => x.GetType().GetProperties());
foreach (var item in properties)
MyHTML = MyHTML.Replace("{" + item.Key.Name + "}", typeof(Person).GetProperty(item.Key.Name).GetValue(p).ToString());
Console.WriteLine(MyHTML);
output: 0, hello, world, 15
Related
I have DataTable
public static DataTable SubProjects = new DataTable();
public static DataTable GetSubProjects
{
get { return SubProjects; }
}
it comes from database so columns are as follows
select ProjectN,ProjectSubN,ProjectM
and list:
public class SubProjectsList
{
public string ProjectNumber { get; set; }
public string SubProjectNumber { get; set; }
public string SubProjectName { get; set; }
public string ProjectManager { get; set; }
public int ObjectID { get; set; }
public SubProjectsList(string ProjectNumber, string SubProjectNumber, string SubProjectName, string ProjectManager, int ObjectID)
{
this.ProjectNumber = ProjectNumber;
this.SubProjectNumber = SubProjectNumber;
this.SubProjectName = SubProjectName;
this.ProjectManager = ProjectManager;
this.SubProjectNumber = SubProjectNumber;
this.ObjectID = ObjectID;
}
}
public static List<SubProjectsList> DeliverySubProjectList = new List<SubProjectsList>();
Ultimately I would like to get all results to new List where
ProjectN + ProjectSubN (DataTable) = ProjectNumber + SubProjectNumber (List) &&
ProjectM (DataTable) != ProjectManager (List)
As an output I need to get ProjectNumber, SubProjectNumber, ProjectM, ProjectManager. Basically I need to catch ProjectM and ProjectManager that does not match for one sub-project. I could have done it this way
var SubProjectResult = SubProjects.AsEnumerable()
.Where(x => DeliverySubProjectList.Any(y =>
y.ProjectNumber.Trim() + y.SubProjectNumber == x.Field<string>("ProjectN").Trim() + x.Field<string>("ProjectSubN").Trim() &&
!string.Equals(y.ProjectManager.Trim(), x.Field<string>("ProjectM").Trim(), StringComparison.CurrentCultureIgnoreCase)))
.Select(x => new
{
ProjectN = x.Field<string>("ProjectN"),
ProjectN = x.Field<string>("ProjectSubN"),
ProjectM = x.Field<string>("ProjectM"),
ProjectM = x.ProjectManager // This is not possible
})
.OrderBy(o => o.ProjectN).ToList();
but
ProjectM = x.ProjectManager // This is not possible
is wrong and does not compile. So I can't get ProjectManager from List where ProjectN + ProjectSubN (DataTable) = ProjectNumber + SubProjectNumber (List) match but ProjectManager does not match. Do I need to Concat DataTable and List together first? I have tried this
var myList1 = SubProjects.AsEnumerable().Concat(DeliverySubProjectList).ToList();
but getting an error
Severity Code Description Project File Line Suppression State
Error CS1929 'EnumerableRowCollection' does not contain a
definition for 'Concat' and the best extension method overload
'Queryable.Concat(IQueryable,
IEnumerable)' requires a receiver of type
'IQueryable'
I think this should give you what you are looking for. You might need to add extra null checks and I am also not very sure about that's how you want to add string values from the list and the data table:
public class ResultModel
{
public string ProjectNumber { get; set; }
public string SubProjectNumber { get; set; }
public string ProjectM { get; set; }
public string ProjectManager { get; set; }
}
...
...
static void Main(string[] args)
{
List<SubProjectsList> DeliverySubProjectList = // populate list
DataTable SubProjects = // populate datatable
List<ResultModel> results = GetResults(DeliverySubProjectList, SubProjects).ToList();
}
private static IEnumerable<ResultModel> GetResults(List<SubProjectsList> DeliverySubProjectList, DataTable SubProjects)
{
return SubProjects.AsEnumerable().Select(row =>
{
var listItem = DeliverySubProjectList.FirstOrDefault(item =>
{
return item.ProjectNumber + item.SubProjectNumber == row["ProjectN"].ToString() + row["ProjectSubN "].ToString()
&& !item.ProjectManager.Equals(row["ProjectM"].ToString());
});
if (listItem != null)
{
return new ResultModel
{
ProjectNumber = row["ProjectN"].ToString(),
SubProjectNumber = listItem.SubProjectNumber,
ProjectM = row["ProjectM"].ToString(),
ProjectManager = listItem.ProjectManager
};
}
else
{
return null;
}
})
.Where(res => res != null);
}
I have the following model that has these fields:
[Key]
public string Id { get; set; }
[IsSearchable]
public string Code{ get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string Address { get; set; }
[IsSearchable]
public string PostCode { get; set; }
[IsFilterable]
public int? Setting{ get; set; }
[IsFilterable, IsSortable]
public Location Location { get; set; }
I am writing a method to compare whether values in a database match this model. So far it looks like this:
private bool CompareEquality(Index resultBody, Type indexType)
{
var properties = indexType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
List<PropertyInfo> searchableProperties = new List<PropertyInfo>();
List<PropertyInfo> filterableProperties = new List<PropertyInfo>();
List<PropertyInfo> sortableProperties = new List<PropertyInfo>();
if (properties.Count() == resultBody.Fields.Count)
{
foreach (var property in properties)
{
var isSearchableAttribute = property.GetCustomAttribute<IsSearchableAttribute>();
var isFilterableAttribute = property.GetCustomAttribute<IsFilterableAttribute>();
var isSortableAttribute = property.GetCustomAttribute<IsSortableAttribute>();
if (isSearchableAttribute != null)
{
searchableProperties.Add(property);
}
if (isFilterableAttribute != null)
{
filterableProperties.Add(property);
}
if (isSortableAttribute != null)
{
sortableProperties.Add(property);
}
}
CheckAttributeEquality(searchableProperties, filterableProperties, sortableProperties);
}
return false;
}
The CheckAttributeEquality method:
private bool CheckAttributeEquality(List<PropertyInfo> searchableProperties, List<PropertyInfo> filterableProperties, List<PropertyInfo> sortableProperties)
{
if (searchableProperties.Count == 4 && filterableProperties.Count == 2 && sortableProperties.Count == 1)
{
CheckPropertyFields(searchableProperties, filterableProperties, sortableProperties);
return true;
}
return false;
}
As I started to write a method to check that the field names match, like so:
foreach (var property in searchableProperties)
{
if (property.Name == "Id" ||)
{
...
}
if (property.Name == "Code")
{
...
}
// etc
I realised how messy and long-winded this whole approach is. I am not hugely experienced in C# and would appreciate any advice as to how I can refactor this up a little bit? I want to check for attribute and name matches.
you could use the Typedescriptor (using System.ComponentModel) for that. Try this:
var pdc = TypeDescriptor.GetProperties( this ); //or your model object, if its not "this"
foreach (var property in searchableProperties)
{
var descriptors = pdc[ property.Name ];
// check if your searchable descriptor is there, and do error handling
}
Once it works, you could also try to solve it with LINQ.
is there a way to make a comma separated from an object. Note its object not List of object
Ex:
public class EmployeeLogReportListViewModel
{
public DateTime Date { get; set; }
public int EmployeeID { get; set; }
public TimeSpan Time { get; set; }
public int Sort { get; set; }
public string Employer { get; set; }
}
With the following values
Date = "2018/02/03"
EmployeeID = 111
Time = 11:53 AM
Sort = 1
Employer = EMP
this should result into
2018/02/03,111,11:53 AM,1 EMP
What is the best way to make this. possible single line of code cause i dont want to use string builder and append all of it.
I think you are looking for Overridden .ToString() method. you have to modify the class like this:
public class EmployeeLogReportListViewModel
{
public DateTime Date { get; set; }
public int EmployeeID { get; set; }
public TimeSpan Time { get; set; }
public int Sort { get; set; }
public string Employer { get; set; }
public override string ToString()
{
return String.Format("{0},{1},{2},{3},{4}", this.Date, this.EmployeeID, this.Time, this.Sort, this.Employer);
}
}
Usage Example:
EmployeeLogReportListViewModel objVm = new EmployeeLogReportListViewModel();
// Assign values for the properties
objVm.ToString(); // This will give you the expected output
Challenge accepted
var o = new EmployeeLogReportListViewModel();
var text = string.Join
(
",",
typeof(EmployeeLogReportListViewModel)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select
(
prop => prop.GetValue(o).ToString()
)
);
Console.WriteLine(text);
Technically that is one line.
If you want the properties sorted alphabetically, you could use this:
var o = new EmployeeLogReportListViewModel();
var text = string.Join
(
",",
typeof(EmployeeLogReportListViewModel)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.OrderBy( prop => prop.Name )
.Select
(
prop => prop.GetValue(o).ToString()
)
);
Console.WriteLine(text);
It's a bit late to reply, but I can imagine you want to do that in order to have some kind of csv output style
a nice and generic way to do it is to create a extension method that transform any Enumerable into a csv string.
so borrowing #John Wu's solution, we come up with something like
public static class EnumerableToCsvExtension
{
public static string ToCSVString<TContent>(this IEnumerable<TContent> enumerable, char propertySeparator = ',', bool includeHeader = true)
{
var properties = typeof(TContent).GetProperties(BindingFlags.Instance | BindingFlags.Public);
var header = string.Join(propertySeparator, properties.Select(p => p.Name));
var rows = enumerable.ToList().ConvertAll(item => string.Join(propertySeparator, properties.Select(p => p.GetValue(item) ?? string.Empty )));
var csvArray = includeHeader ? rows.Prepend(header) : rows;
return string.Join(Environment.NewLine, csvArray);
}
}
then you use it like
var list = new List<EmployeeLogReportListViewModel> { new EmployeeLogReportListViewModel(), new EmployeeLogReportListViewModel() };
list.ToCSVString();
list.ToCSVString(propertySeparator: '|', includeHeader: false);
You can use like below:
public class bKashAccountInfo
{
public string bKashNumber { get; set; }
public string bKashAccountType { get; set; }
public override string ToString()
{
return String.Format($"bKash Number-{bKashNumber}, Account Type - {bKashAccountType}");
}
}
I have a List containing a string property and another List.
How can I in a datagrid display both the string property AND the properties from the containing List?
public class Main
{
public string Name { get; set; }
public List<Info> Info { get; set; }
}
public class Info
{
public string Info1 { get; set; }
public string Info2 { get; set; }
}
I would like my datagrid to show something like this:
#Name #Info1 #Info2
name info1 info2
name info1 info2
Thank you very much in advance.
The static way if you can change the Main class is to provide string-properties of the properties in the other class that you want to expose:
public class Main
{
public string Name { get; set; }
public List<Info> Info { get; set; }
public string Info1 {
get {
return Info == null || Info.Count == 0 ? ""
: Info.Last().Info1;
}
}
public string Info2
{
get
{
return Info == null || Info.Count == 0 ? ""
: Info.Last().Info2;
}
}
}
Other approaches:
you could use a new class just for this requirement
you could use an anonymous type:
this.dataGridView1.DataSource = mains
.Select(m => new {
m.Name,
Info1 = m.Info.Last().Info1,
Info2 = m.Info.Last().Info2, // add null or count=0 check here
}).ToList();
If you want to take the last record from the Info list for each Main item you can write:
var q = from m in mainList
let i = m.Info.LastOrDefault()
select new
{
m.Name,
Info1 = i == null ? null : i.Info1,
Info2 = i == null ? null : i.Info2,
};
say there is a class like
class phones
{
public int Id {get; set;}
public string Name {get; set;}
public string Color {get; set;}
public decimal Price {get; set;}
}
List<phones> myList = GetData();
//list is filled with objects
Now, I know the Id and the exact name of the object's property and want to get the value from the matching object.
private string GetValue(int pid, string featurename)
{
string val = "";
foreach(phones obj in myList)
{
if(obj.Id == pid)
{
//if featurename is 'Name', it should be
//val = obj.Name;
//if featurename is 'Price', it should return
//val = obj.Price;
break;
}
}
return val;
}
Is this possible. Please advise.
Use this:
Phones phones= new Phones();
string returnValue = phones.GetType().GetProperty(featureName).GetValue(phones, null).ToString();
Also, remember to add validation for input featureName and error handling.
How about this:
foreach(phones obj in myList)
{
if(obj.Id == pid)
{
if (featurename == "Name")
{
return obj.Name;
}
else if (featurename == "Price")
{
return obj.Price.ToString();
}
else
{
return string.Empty;
}
}
}
I think you want Property with given featurename and use it
you could use lambda expression like this
or
Use PropertyInfo like this
foreach (PropertyInfo p in typeof(ClassName).GetProperties())
{
string propertyName = p.Name;
//....
}