at present, I have:
string outputRow = string.Empty;
foreach (var entityObject in entityObjects)
{
outputRow = entityObject.field1 + "," + entityObject.Field2 etc....
}
I'm still new to the Entity Framework, is there a quicker way?
Sample code that shows a simple yet powerful way of accomplishing what you want with no need to hard code property names (using reflection):
/// <summary>
/// Creates a comma delimeted string of all the objects property values names.
/// </summary>
/// <param name="obj">object.</param>
/// <returns>string.</returns>
public static string ObjectToCsvData(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj", "Value can not be null or Nothing!");
}
StringBuilder sb = new StringBuilder();
Type t = obj.GetType();
PropertyInfo[] pi = t.GetProperties();
for (int index = 0; index < pi.Length; index++)
{
sb.Append(pi[index].GetValue(obj, null));
if (index < pi.Length - 1)
{
sb.Append(",");
}
}
return sb.ToString();
}
More on this:
Objects to CSV
How can i convert a list of objects to csv
Are there any CSV readers/writer lib’s in c#
Writing a CSV file in .net
LINQ to CSV : Getting data the way you want
LINQ to CSV library
I took Leniel's suggestion and wrapped it up in a full featured "writer" that also allows you to filter the properties you want written. Here's the code for your usage:
public class CsvFileWriter
{
public static void WriteToFile<T>(string filePath, List<T> objs, string[] propertyNames)
{
var builder = new StringBuilder();
var propertyInfos = RelevantPropertyInfos<T>(propertyNames);
foreach (var obj in objs)
builder.AppendLine(CsvDataFor(obj, propertyInfos));
File.WriteAllText(filePath, builder.ToString());
}
public static void WriteToFileSingleFieldOneLine<T>(string filePath, List<T> objs, string propertyName)
{
var builder = new StringBuilder();
var propertyInfos = RelevantPropertyInfos<T>(new[] { propertyName });
for (var i = 0; i < objs.Count; i++)
{
builder.Append(CsvDataFor(objs[i], propertyInfos));
if (i < objs.Count - 1)
builder.Append(",");
}
File.WriteAllText(filePath, builder.ToString());
}
private static List<PropertyInfo> RelevantPropertyInfos<T>(IEnumerable<string> propertyNames)
{
var propertyInfos = typeof(T).GetProperties().Where(p => propertyNames.Contains(p.Name)).ToDictionary(pi => pi.Name, pi => pi);
return (from propertyName in propertyNames where propertyInfos.ContainsKey(propertyName) select propertyInfos[propertyName]).ToList();
}
private static string CsvDataFor(object obj, IList<PropertyInfo> propertyInfos)
{
if (obj == null)
return "";
var builder = new StringBuilder();
for (var i = 0; i < propertyInfos.Count; i++)
{
builder.Append(propertyInfos[i].GetValue(obj, null));
if (i < propertyInfos.Count - 1)
builder.Append(",");
}
return builder.ToString();
}
}
string csv = "";
//get property names from the first object using reflection
IEnumerable<PropertyInfo> props = entityObjects.First().GetType().GetProperties();
//header
csv += String.Join(", ",props.Select(prop => prop.Name)) + "\r\n";
//rows
foreach(var entityObject in entityObjects)
{
csv += String.Join(", ", props.Select(
prop => ( prop.GetValue(entityObject, null) ?? "" ).ToString()
) )
+ "\r\n";
}
Would be better to use StringBuilder for lots of entitys
The code doesn't check for when entityObjects is empty
Related
The list contains two objects or more I want to create string with the name of its properties and values.
async Task<string> ICsvHandler.CsvHandler()
{
var employees = new List<objectModel>();
employees.Add(new objectModel("1234", "543", "", "7865", "filhal null"));
employees.Add(new objectModel("1234", "543", "0000", "7865", "filhal null"));
Console.WriteLine(employees);
ExportCsv(employees);
return null;
}
void ExportCsv<T>(List<T> genericList)
{
try
{
string fileName = $"{ DateTime.Now.ToUniversalTime().ToString("yyyyMMDDHHmmss") }{ ".csv" }";
var sb = new StringBuilder();
foreach (var prop in typeof(T).GetProperties())
header += prop.Name + ",";
header = header.Substring(0, header.Length - 2);
sb.AppendLine(header);
foreach (var obj in genericList)
{
var line = "";
foreach (var prop in info)
line += prop.GetValue(obj, null)+"," ;
line = line.Substring(0, line.Length - 2);
sb.AppendLine(line);
char [] ch= sb.ToString().ToCharArray();
_cloudStorageService.UploadFileFromMemoryAsync(fileName,ch);
}
}
catch (System.Exception)
{
throw;
}
}
I am getting name of the property when I run but not the values of object.
I want to understand why my values are not coming into the StringBuilder with line += prop.GetValue(obj, null) + ",";. this only shows ,,,,,,,,,
Expecting name of property along with its values but getting name in header and not values.
I am pulling some data from a BigQuery table using the code below in C#
BigQueryClient client = BigQueryClient.Create("<Project Name>");
BigQueryTable table = client.GetTable("<Database>", "Students");
string sql = $"select * FROM {table} where Marks='50'";
BigQueryResults results = client.ExecuteQuery(sql);
foreach (BigQueryRow row in results.GetRows())
{
}
I want to be able to either read the entire results variable into JSON or be able to get the JSON out of each row.
Of course, I could create a class that models the table. And inside the foreach loop, I could just read each row into the class object. The class object I can try to serialize into JSON using a third party like "newton soft".
Something like :
class Student{
int id; // assume these are columns in the db
string name;
}
My foreach would now look like:
foreach (BigQueryRow row in results.GetRows())
{
Student s=new Student();
s.id = Convert.ToString(row["id"]);
s.name= Convert.ToString(row["name"]);
// something like string x=x+ s.toJSON(); //using newton soft
}
This way string x will have the JSON generated and appended for each row.
Or is there a way I can just add each student to a collection or List and then get the JSON from the whole list?
This whole reading row by row and field by field seems tedious to me and there must be a simpler way I feel. Did not see any support from Google BigQuery for C# to directly convert to JSON. They did have something in Python.
If not then the list to JSON would be better but I am not sure if it supported.
Update :
https://github.com/GoogleCloudPlatform/google-cloud-dotnet/blob/master/apis/Google.Cloud.BigQuery.V2/Google.Cloud.BigQuery.V2/BigQueryRow.cs
Looks like the Big Query Row class has a RawRow field which is of Type TableRow. And the class uses JSON references so , I am sure they have the data of the row in JSON format . How can I expose it to me ?
This might be a little late but you can use:
var latestResult = _bigQueryClient.ExecuteQuery($"SELECT TO_JSON_STRING(t) FROM `{ProjectId}.{DatasetId}.{TableName}` as t", null
All columns will be serialized as json and placed in the first column on each row. You can then use something like Newtonsoft to parse each row easily.
I ran into the same issue.
I am posting this solution which is not optimized for performance but very simple for multiple data types.
This allows you to deserialize anything (almost)
public class BQ
{
private string projectId = "YOUR_PROJECT_ID";
public BQ()
{
}
public List<T> Execute<T>(string sql)
{
var client = BigQueryClient.Create(projectId);
List<T> result = new List<T>();
try
{
string query = sql;
BigQueryResults results = client.ExecuteQuery(query, parameters: null);
List<string> fields = new List<string>();
foreach (var col in results.Schema.Fields)
{
fields.Add(col.Name);
}
Dictionary<string, object> rowoDict;
foreach (var row in results)
{
rowoDict = new Dictionary<string, object>();
foreach (var col in fields)
{
rowoDict.Add(col, row[col]);
}
string json = Newtonsoft.Json.JsonConvert.SerializeObject(rowoDict);
T o = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
result.Add(o);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
client.Dispose();
Console.WriteLine("Done.");
}
return result;
}
}
You can use Newtonsoft.Json. First download by PackageManager Console the Nuget Package, here you can get the command to do that.
After download you can use it as the following code:
List<Student> list = new List<Student>();
foreach (BigQueryRow row in results.GetRows())
{
Student s=new Student();
s.id = Convert.ToString(row["id"]);
s.name= Convert.ToString(row["name"]);
list.Add(s);
}
var jsonResult = Newtonsoft.Json.JsonConvert.SerializeObject(list);
I hope this can help you.
Here is the complete solution for casting BigQueryResults or GetQueryResultsResponse or QueryResponse data to Model/JSON format using C# reflection:
public List<T> GetBQAsModel<T>(string query) where T : class, new()
{
var bqClient = GetBigqueryClient();
var res = bqClient.ExecuteQuery(query, parameters: null);
return GetModels<T>(res);
}
private List<T> GetModels<T>(BigQueryResults tableRows) where T : class, new()
{
var lst = new List<T>();
foreach (var item in tableRows)
{
var lstColumns = new T().GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
var newObject = new T();
for (var i = 0; i < item.RawRow.F.Count; i++)
{
var name = item.Schema.Fields[i].Name;
PropertyInfo prop = lstColumns.FirstOrDefault(a => a.Name.ToLower().Equals(name.ToLower()));
if (prop == null)
{
continue;
}
var val = item.RawRow.F[i].V;
prop.SetValue(newObject, Convert.ChangeType(val, prop.PropertyType), null);
}
lst.Add(newObject);
}
return lst;
}
private List<T> GetModels<T>(GetQueryResultsResponse getQueryResultsResponse) where T : class, new()
{
var lst = new List<T>();
foreach (var item in getQueryResultsResponse.Rows)
{
var lstColumns = new T().GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
var newObject = new T();
for (var i = 0; i < item.F.Count; i++)
{
var name = getQueryResultsResponse.Schema.Fields[i].Name;
PropertyInfo prop = lstColumns.FirstOrDefault(a => a.Name.ToLower().Equals(name.ToLower()));
if (prop == null)
{
continue;
}
var val = item.F[i].V;
prop.SetValue(newObject, Convert.ChangeType(val, prop.PropertyType), null);
}
lst.Add(newObject);
}
return lst;
}
private List<T> GetModels<T>(QueryResponse queryResponse) where T : class, new()
{
var lst = new List<T>();
foreach (var item in queryResponse.Rows)
{
var lstColumns = new T().GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
var newObject = new T();
for (var i = 0; i < item.F.Count; i++)
{
var name = queryResponse.Schema.Fields[i].Name;
PropertyInfo prop = lstColumns.FirstOrDefault(a => a.Name.ToLower().Equals(name.ToLower()));
if (prop == null)
{
continue;
}
var val = item.F[i].V;
prop.SetValue(newObject, Convert.ChangeType(val, prop.PropertyType), null);
}
lst.Add(newObject);
}
return lst;
}
I would do something like this:
var res = Result. Getrows. Select(x=> new student(){id=x[`ID']}).
And then:
var js = json. Conver(res);
This way is much faster and clearer.
I'm posting data to a service that requires that I submit duplicate query string keys (ugly and not specified in any standards).
I'm using WebClient object to build the request. I'd like to keep using it since it is used frequently elsewhere in the project.
When I do this
foreach(var f in formats)
client.QueryString.Add("formats", f);
I get a list &formats=format_1,format_2,format_3 which the service does not support.
Is there a better alternative than this old-school ugliness:
var extraQueryString = string.Empty;
extraQueryString += "?apiKey=" + TRANSCODE_KEY;
extraQueryString += "&fileKey=" + fileKey;
foreach (var f in formats)
extraQueryString += "&formats=" + f;
var response = client.UploadData(TRANSCODE_URI + "task" + extraQueryString , new byte[] { });
The reason for this is because the NameValueCollection separates duplicate keys with commas. You could extend the NameValueCollection and override the Get method and have it return the format you want.
public class DupeNVC: NameValueCollection
{
private string _duplicateKey;
public DupeNVC(string duplicateKey = null)
{
_duplicateKey = duplicateKey;
}
public override string Get(int index)
{
//check if duplicate key has been specified
//if not, then call the default Get implementation
if (!String.IsNullOrEmpty(_duplicateKey))
{
ArrayList list = (ArrayList)base.BaseGet(index);
int num = (list != null) ? list.Count : 0;
if (num == 1)
{
return (string)list[0];
}
if (num > 1)
{
StringBuilder stringBuilder = new StringBuilder((string)list[0]);
for (int i = 1; i < num; i++)
{
//format our string and append the duplicate key specified
stringBuilder.AppendFormat("&{0}=", _duplicateKey);
stringBuilder.Append((string)list[i]);
}
return stringBuilder.ToString();
}
return null;
}
else
return base.Get(index);
}
}
You can use it like a normal NameValueCollection but if you pass in a duplicate strning in the constructor, it will look for that duplicate key and run the modified code above (otherwise it will just use the default base.Get method.
DupeNVC dnvc = new DupeNVC("formats");
foreach(var f in formats)
dnvc.Add("formats", f);
webClient.QueryString = dnvc;
This hasn't been fully tested but it should output the querystring format you want. Of course, this could be extended further by taking a collection of duplicate keys but this was just to give you an idea for your current problem.
Here's my take on this. WebClient essentially works like the ToString method of this class; it gets all the keys and then retrieves the values one at a time, doing a concatenate. So I override the AllKeys to return an array with repeated elements..
For example if a particular key has multiple values:
nvc["hello"] = { "a", "b", "c" }
Then my AllKeys will return an array with "hello" 3 times. WebClient will naively request it 3 times. A Dictionary tracks how many times a "hello" has been requested, and returns a different one each time (pseudo enumerator)
public class ParrotingNameValueCollection : NameValueCollection
{
Dictionary<string, int> _indexTracker = new Dictionary<string, int>();
public override string[] AllKeys
{
get
{
var l = new List<string>();
foreach (var k in base.AllKeys)
{
foreach (var x in (ArrayList)base.BaseGet(k))
l.Add(k);
_indexTracker[k] = 0;
}
return l.ToArray();
}
}
public override string Get(string name)
{
var list = (ArrayList)base.BaseGet(name);
var toReturn = (string)list[_indexTracker[name]];
_indexTracker[name]++;
return toReturn;
}
public override string ToString()
{
string delimiter = String.Empty;
StringBuilder values = new StringBuilder();
foreach (string name in this.AllKeys)
{
values.Append(delimiter);
values.Append((name));
values.Append("=");
values.Append((this[name]));
delimiter = "&";
}
return values.ToString();
}
}
I wrote a method that accepts a generic parameter and then it prints its properties. I use it to test my web service. It's working but I want to add some features that I don't know how to implement. I want to print values of lists, because now it just writes System.Collection.Generic.List1 which is expected.
Here is my code so far, this is working for basic types (int, double etc.):
static void printReturnedProperties<T>(T Object)
{
PropertyInfo[] propertyInfos = null;
propertyInfos = Object.GetType().GetProperties();
foreach (var item in propertyInfos)
Console.WriteLine(item.Name + ": " + item.GetValue(Object).ToString());
}
You could do something like this:
static void printReturnedProperties(Object o)
{
PropertyInfo[] propertyInfos = null;
propertyInfos = o.GetType().GetProperties();
foreach (var item in propertyInfos)
{
var prop = item.GetValue(o);
if(prop == null)
{
Console.WriteLine(item.Name + ": NULL");
}
else
{
Console.WriteLine(item.Name + ": " + prop.ToString());
}
if (prop is IEnumerable)
{
foreach (var listitem in prop as IEnumerable)
{
Console.WriteLine("Item: " + listitem.ToString());
}
}
}
}
It will then enumerate through any IEnumerable and print out the individual values (I'm printing them one per line, but obviously, you can do different.)
The elements inside a list can be retrieved through the indexer property Item. This property accepts an index argument (there is an overload of PropertyInfo.GetValue that accept an object array, just like MethodInfo.Invoke) and returns the object at that position.
int index = /* the index you want to get here */;
PropertyInfo indexer = Object.GetProperty("Item");
object item = indexer.GetValue(Object, new object[] { index });
I usually prints list with a , between each item.
To make that easy I have created a simple extension method:
public static class ListEx
{
public static string StringJoin<T>(this IEnumerable<T> items)
{
return string.Join(", ", items);
}
}
Call the method as myList.StringJoin().
You can of course modify the method to use another delimiter och call string.Join directly.
Here is a snippet, assuming that your List is of Type T.
foreach (PropertyInfo item in propertyInfos)
{
Object obj = item.GetValue(object,null);
if (!obj.GetType().IsValueType)
{
if (obj.GetType() == typeof(String))
{
Console.WriteLine(obj.ToString());
}
else if (obj.GetType() == typeof(List<T>))
{
//run a loop and print the list
}
else if (obj.GetType().IsArray) // this means its Array
{
//run a loop to print the array
}
}
else
{
//its primitive so we will convert to string
Console.WriteLine(obj.ToString());
}
I think you want something like this:
public class Program
{
public static void PrintProperties<T>(T t)
{
var properties = t.GetType().GetProperties();
foreach (var property in properties)
{
var name = property.Name;
var value = property.GetValue(t, null);
if (property.PropertyType.IsGenericType && property.PropertyType == typeof(IEnumerable<>))
{
var formatList = typeof(Program).GetMethod("FormatList", new[] { value.GetType() });
// value.GetType().GetGenericArguments().First() will get you the underlying type of the list,
// i.e., the TItemType where the property you are currently
// handling is of type IEnumerable<TItemType>
formatList.MakeGenericMethod(value.GetType().GetGenericArguments().First());
value = formatList.Invoke(null, new object[] { value });
Console.Out.WriteLine(name + ": " + value);
}
else
{
Console.Out.WriteLine(name + ": " + value);
}
}
}
public static string FormatList<TPlaceholder>(IEnumerable<TPlaceholder> l)
{
return string.Join(", ", l);
}
}
The code is untested but basically, you want to tackle enumerable types differently as compared to scalar values, so once you hit something of the type IEnumerable<TItemType>, you make a call to the FormatList<TPlaceholder> method.
Now, bear in mind that your original T and TItemType are not necessarily the same. When you invoke FormatList using reflection, you want to bind the TPlaceholder to TItemType. Once you have done that, you just invoke the formatting method and pass it the actual instance of the list, which returns you a string. That string you can then just output.
Hope that helps.
Borrowing heavily on the above examples, here is my full solution. This has been tested and handles IEnumerable's being passed in by printing out the properties of each element. It's not recursive but that's easy enough to add.
Be aware that many of the above examples will crash with indexed properties (lists for example).
Parameter count mismatch in property.GetValue().
It's avoided here by filtering properties which have indexed properties using this bit of LINQ.
Where(x=>!x.GetIndexParameters().Any())
Full example in the form of an extension method below.
/// <summary>
/// Returns string representation of object property states i.e. Name: Jim, Age: 43
/// </summary>
public static string GetPropertyStateList(this object obj)
{
if (obj == null) return "Object null";
var sb = new StringBuilder();
var enumerable = obj as IEnumerable;
if (enumerable != null)
{
foreach (var listitem in enumerable)
{
sb.AppendLine();
sb.Append(ReadProperties(listitem));
}
}
else
{
sb.Append(ReadProperties(obj));
}
return sb.ToString();
}
private static string ReadProperties(object obj)
{
var sb = new StringBuilder();
var propertyInfos = obj.GetType().GetProperties().OrderBy(x => x.Name).Where(x=>!x.GetIndexParameters().Any());
foreach (var prop in propertyInfos)
{
var value = prop.GetValue(obj, null) ?? "(null)";
sb.AppendLine(prop.Name + ": " + value);
}
return sb.ToString();
}
We have some code that exports data from a database to Excel and it works well.
The main problem is we use a DB view to gather all our data. This creates the issue of having a sizable view as we have multiple types of objects of which we export.
class Event
int id
string title
List<EventDate> Dates
string desc
class EventDate
DateTime start
DateTime end
List<EventLocation> Locations
class EventLocation
string address
string city
string zip
class Birthday : Event
int balloonsOrdered
string cakeText
class Meeting : Event
string Organizer
string Topic
So, above is the model. Birthday and Meeting inherit from Event and all Event objects have a list of EventDate objects. Each EventDate object has a start date, end date and a list of Location objects.
Our goal is to find a dynamic way to get data from the DB to an Excel doc. We'd rather not maintain a massive view in the database (as we'll add more event types eventually).
I'm not all that familiar with .NET's XML capabilities, but the solution we are using now uses OpenXML and the code makes sense.
You could create a CSV file using List<T> and and following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Web;
public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
if (list == null || list.Count == 0) return;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
using (StringWriter sw = new StringWriter())
{
//gets all properties
PropertyInfo[] props = t.GetProperties();
//this is the header row
//foreach of the properties in class above, write out properties
foreach (PropertyInfo pi in props)
{
sw.Write(pi.Name.ToUpper() + ",");
}
sw.Write(newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
foreach (PropertyInfo Column in props)
{
//this is the row+col intersection (the value)
string value = item.GetType().GetProperty(Column.Name).GetValue(item, null).ToString();
if (value.Contains(","))
{
value = "\"" + value + "\"";
}
sw.Write(value + ",");
}
sw.Write(newLine);
}
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}
I improved the solution from Brad to better work with Entity Framework Data Annotations :
it gets the display name from annotations instead of the column name
it does not export columns that are marked "scaffold = false" via annotations
it offers you a way to deal with complex types (not primitive types)
public static class ExportListToCSV
{
public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
if (list == null || list.Count == 0) return;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
//gets all columns
PropertyInfo[] columns = t.GetProperties();
// skip columns where ScaffoldColumn = false
// useful to skip column containing IDs for Foreign Keys
var props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(p => new
{
Property = p,
Attribute = p.GetCustomAttribute<ScaffoldColumnAttribute>()
})
.Where(p => p.Attribute == null || (p.Attribute != null && p.Attribute.Scaffold != false))
.ToList();
using (StringWriter sw = new StringWriter())
{
//this is the header row
foreach (var prop in props)
{
var pi = prop.Property;
string columnName = "";
// outputs raw column name, but this is not really meaningful for the end user
//sw.Write(pi.Name + ",");
columnName = pi.GetDisplayName();
sw.Write(columnName + ",");
}
sw.Write(newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
foreach (var prop in props)
{
var column = prop.Property;
//this is the row+col intersection (the value)
PropertyInfo info = item.GetType().GetProperty(column.Name);
//string value = info.GetValue(item, null);
string value = GetDescriptionForComplexObjects(info.GetValue(item, null));
if (value.Contains(","))
{
value = "\"" + value + "\"";
}
sw.Write(value + ",");
}
sw.Write(newLine);
}
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}
private static string GetDescriptionForComplexObjects(object value)
{
string desc = "";
if (
(value != null && !IsSimpleType(value.GetType()))
)
{
dynamic dynObject = value;
if (dynObject != null)
{
//using Model Extensions,
//I made sure all my objects have a DESCRIPTION property
//this property must return a string
//for an employee object, you would return the employee's name for example
//desc = dynObject.DESCRIPTION;
}
}
else
{
desc = "" + value;
}
return desc;
}
public static string GetDisplayName(this PropertyInfo pi)
{
if (pi == null)
{
throw new ArgumentNullException(nameof(pi));
}
return pi.IsDefined(typeof(DisplayAttribute)) ? pi.GetCustomAttribute<DisplayAttribute>().GetName() : pi.Name;
}
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]))
;
}
}
}