I created an extension method to convert a datatable to list and list to datatable. I have multiple problems with this. can someone help me fix the issues please:
Both datatable columns and the generic class property names needs to be the same even and case sensitive. I need to modify this to handle the situation where case is not taken into consideration ex: EmployeeName = employeename.
If the generic class has a complex type as a property then my function does not seem to work. ex: if i have Public string EmployeeName {get; set;} my code works, but if i have Public Department DepartmentDetails {get; set;} ( know this one is a bit tricky but if someone can give me a suggestion how to handle this at all, i shall be glad.)
Please find below my Extension method.
public static List<T> ToList<T>(this DataTable table) where T : new()
{
try
{
var dataList = new List<T>();
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
var propertyList = (from PropertyInfo property in typeof(T).GetProperties(flags)
select new
{
Name = property.Name,
Type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType
}).ToList();
var dataTableFieldNames = (from DataColumn columnHeader in table.Columns
select new { Name = columnHeader.ColumnName, Type = columnHeader.DataType }).ToList();
var commonProperties = propertyList.Intersect(dataTableFieldNames).ToList();
foreach (DataRow dataRow in table.AsEnumerable().ToList())
{
var templateType = new T();
foreach (var field in commonProperties)
{
PropertyInfo propertyInfos = templateType.GetType().GetProperty(field.Name);
propertyInfos.SetValue(templateType, dataRow[field.Name], null);
}
dataList.Add(templateType);
}
return dataList;
}
catch (Exception ex)
{
throw;
}
}
Any help is highly appreciated. Cheers!!!
Both datatable columns and the generic class property names needs to be the same even and case sensitive. I need to modify this to handle the situation where case is not taken into consideration ex: EmployeeName = employeename.
That part is easy. You can compare the Name's case-insensitively:
var commonProperties = propertyList
.Where(p => dataTableFieldNames
.Any(d => string.Equals(d.Name, p.Name, StringComparison.OrdinalIgnoreCase) &&
d.Type == p.Type).ToList();
If the generic class has a complex type as a property then my function does not seem to work.
Now that is a bit hard and depends on what would you like to do in that situation? Does your DataTable also contains columns that belongs the other types (e.g. Departmant) ? If so you will need to determine which types of properties you have (additionally,other than the built-in types) and which additional columns exists in your DataTable for those types.Then you can get the properties of each type and map them to the columns in your DataTable.
Related
I have found below code on stackoverflow. But i am not getting what fundamentally this code is doing. Can anyone please explain me how this code works?
public static List<T> ToListof<T>(DataTable dt)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
var objectProperties = typeof(T).GetProperties(flags);
var targetList = dt.AsEnumerable().Select(dataRow =>
{
var instanceOfT = Activator.CreateInstance<T>();
foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
{
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
}
return instanceOfT;
}).ToList();
return targetList;
}
Specially i want to know at where coloumn's data is getting type casted. I have searched on many links but i am not getting proper answer anywhere.
It attempts to convert a datatable to a list of objects of type T, dynamically at runtime.
var objectProperties = typeof(T).GetProperties(flags);
This line uses Reflection to get a list of public properties on type T.
var targetList = dt.AsEnumerable().Select(dataRow =>
This line iterates the DataTable as an IEnumerable, getting an instance called dataRow for each row.
var instanceOfT = Activator.CreateInstance<T>();
This creates a new instance of type T using reflection, inside the loop. This means a new T is created for each dataRow.
foreach (var properties in objectProperties.Where(properties =>
columnNames.Contains(properties.Name)
This goes over all the properties of T we got back in the beginning, which are also in columnNames - meaning that there's a column with value for them
&& dataRow[properties.Name] != DBNull.Value))
The second half of the condition makes sure that the column has a value and isn't NULL.
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
This uses reflection, again to set the value from the datarow into the property of T.
).ToList();
This takes all the items returned from the Select statement and returns a List from them.
The code isn't the neatest, but the variables are pretty well-named and clear, if you know how reflection works. As for your second question - there's no casting, because this code assumes that the type of the value in the DataRow matches the type of the property. If it doesn't, an exception will be thrown.
In Detail:
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
this will combine the public and the instance flag so that only Public non static methods will be searched.
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
this will list all column names from the data table
var objectProperties = typeof(T).GetProperties(flags);
gets the Type of the generic argument and will list all public, non static properties
dt.AsEnumerable().Select
creates an IEnumerable of each data row in the DataTable
var instanceOfT = Activator.CreateInstance<T>();
this creates a new instance as you would use new
foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
{
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
}
this will iterate through all propertys of T whos also contained in the datatable and not null (eg. DbNull from the database)
then it calls SetValue. As the dataRow will already return the value as its stored in the database there is no cast nessesary. This does only work if the Property and the type in the database are "the same". As NVarchar for string.
I'm looking for a method to getdatabase table's field with variable thing.
I wrote a stupid and unworking method to explain what I need:
using (var dbContext = new db_ReadyEngine_MSSQL())
{
string nameOfField = "UserName";
var table = dbContext.tbl_User;
foreach (var x in table)
{
string fieldValue = x.nameOfField;
}
}
Here, I'm trying to determining column name which it nameOfField...
You may call data from DataTable by using name of column, as example:
Object o = dataTable.Rows[0][nameOfField];
try this:
List<string>values = new List<string>();
using (var dbContext = new db_ReadyEngine_MSSQL())
{
values = (from s in dbContext.tbl_User select s.Username).ToList();
}
return values
Assuming I am reading your question correctly, you want to get the value of a column, whose name is only known at runtime?
If so, have a look at the code below. It will pull the properties from the object type, search for the one that matches the nameOfField value, and then pull attempt to pull a value from it.
foreach (var x in table)
{
var fieldValue = x.GetType().GetProperties().Where(a => a.Name == nameOfField).Select(p => p.GetValue(x, null)).FirstOrDefault();
}
U can use Reflection to get value of Property using its String Name
using (var dbContext = new db_ReadyEngine_MSSQL())
{
string nameOfField = "UserName";
var table = dbContext.tbl_User;
foreach (var x in table)
{
string fieldValue = typeof(x).GetProperty(nameOfField ).GetValue(x, null) as string;
}
}
You can use Entity SQL for this without typing the query itself:
IEnumerable<object> GetFieldValues<T>(DbContext context, string fieldName)
where T : class
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
ObjectQuery<T> q = oc.CreateObjectSet<T>();
return q.Select("it." + fieldName)
.AsEnumerable()
.Select(x => x[0]);
}
The trick is that an ObjectSet (the predecessor, sort of, or DbSet) can easily be cast to an ObjectQuery, which is the base of Entity SQL. By default, the command text uses "it" as alias for the table in the query, so if you want the value of one specific field, you must prefix it by the alias, and there you go.
The Select returns a DbDataRecord. The first value from this record is returned.
The advantage of this method over others is that it only queries the requested field from the database.
Of course, if you know the type of the field in question up front, you can make a strong-typed version of this method.
I'm having trouble figuring out what I'm doing wrong here. I have some LINQ that returns an IQuery object, and later in the code, I'm attempting to list out the attributes returned. This is best explained by this abbreviated code (the actual LINQ is a lot more complex and involves joins - the LINQ itself works fine):
public IQueryable<Object> FindAll()
{
var query = from user in _db.Users
select new
{
id = user.Id,
is_active = user.IsActive,
email = user.Email,
dob = user.Dob,
user.user_type,
};
return query;
}
Elsewhere in the code I have:
query.ConvertToCsv();
Although I have attempted to insert a .ToList() in that call as well.
The ConvertToCsv has:
public static string ConvertToCSV<TSource>(this IEnumerable<TSource> source)
{
StringBuilder sb = new StringBuilder();
var properties = typeof(TSource).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
var enumerable = source as IList<TSource> ?? source.ToList();
if (!enumerable.Any()) return "";
string headerString = "";
foreach (var prop in properties)
{
headerString += (headerString.Length > 0 ? "," : "") + prop.Name;
}
sb.AppendLine(headerString);
foreach (TSource item in enumerable)
{
string line = string.Join(",", properties.Select(p => p.GetValue(item).ToCsvValue()).ToArray());
sb.AppendLine(line);
}
return sb.ToString();
}
Note I have also tried to pull out the property names with this code:
PropertyInfo[] pi = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var properties = pi.OrderBy(x => x.MetadataToken);
foreach (PropertyInfo p in properties)
{ etc etc }
In all cases, the property or field list returns an empty list, and as such, I can't iterate through the object to spit out a header row or data rows. Tracing through all the code and inspecting the variables indicates that everything is fine until I get to the GetProperties/GetFields line and the code fails.
What rookie mistake am I making? Should I be replacing <Object> with something else?
To pass an anonymous type, or a collection that contains anonymous
types, as an argument to a method, you can declare the parameter as
type object. However, doing this defeats the purpose of strong typing.
If you must store query results or pass them outside the method
boundary, consider using an ordinary named struct or class instead of
an anonymous type.
by Anonymous Types (C# Programming Guide)
Create your own class and change method declaration to be IQueryable<MyClass> instead of object
Did you consider doing something like: db.Users.Select(u => new UserDto() { Id = user.Id, Name = ..., where UserDto is dedicated class that has all the properties you'll need in the future? I think you lose information about properties when you cast from anonymous class to an Object. Although, I never tried to obtain member info from anonymous class
All, I have recently localised the entire aplication and I am faced with the following problem;I have the following LINQ query in my application
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(strpcPSPassBegCost)) by new
{
BaseCC = a.Field<string>(strpcBaseCC)
}
into g
select new
{
g.Key.BaseCC,
PSPassBegCost = g.Sum()
};
This is creating a new object ccsum which we use to create a DataTable and subsequently populate an SQL Server database.
The problem is that each of the new items being created in the DataTable with column names BaseCC and PSPassBegCost, but these names do not matched the German version of these names. Now for my question: is there a way to do something like:
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(strpcPSPassBegCost)) by new
{
BaseCC = a.Field<string>(strpcBaseCC)
}
into g
select new
{
g.Key.BaseCC as Resources.BaseCC,
PSPassBegCost = g.Sum() as Resources.PSPassBegCost
};
so that I can name the tables according to their localised names?
Edit. The code that retrieve a DataTable from ccsum is
fooDt = Utils.LINQToDataTable(ccsum);
and
public static DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
PropertyInfo[] oProps = null;
if (varlist == null)
return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create
// table, Only first time, others will follow.
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
Thanks for your time.
Another approach might be to rename the 'columns' in the ccsum object as a post-processing step although the values of sumcc are not populated until they are requested at run-time - any other ideas?
Create class or enum which will map column indexes to some readable names:
public static class SumGLColumn
{
public const int BaseCC = 0;
public const int PSPassBegCost = 1;
}
And use column indexes instead of column names to query your datatable:
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(SumGLColumn.PSPassBegCost))
by a.Field<string>(SumGLColumn.BaseCC) into g
select new {
BaseCCg = g.Key,
PSPassBegCost = g.Sum()
};
Attempting to localize components of your system for greater human comprehension is a laudable goal, but it will give you challenges in using the the vast majority of tools and libraries: generally database tooling expects these things to be constant and ignorant of localization.
If you wish your database tables to be easier to understand, perhaps a more practical solution would be to produce localized views instead? The views could live in a de schema and be one-to-one translations of your tables. This would allow you to leverage a lot of the standard tooling, keeping your system in a consistent "neutral" culture internally (whatever your development culture is) and providing translations over the top of these wherever required.
I think trying to embed this kind of localization into the heart of your system is likely to not be worth the cost of working around the expectations of most developers and toolsets and you're better providing a façade.
This is not possible. In the select statement you define an anonymous type. This is not a language feature, but a compiler-feature, which means that the compiler creates a class for this type with the properties you define.
This means that the compiler must know the names at compile time. If you want something more dynamic, I recommend you to use a dictionary:
select new Dictionary<string, object>
{
{ Resources.BaseCC, g.Key.BaseCC },
{ Resources.PSPassBegCost , g.Sum() }
};
I have seen the reverse of this question quite a few times, but have not seen how to do what I would like.
Suppose I have the following code:
var myNewData = from t in someOtherData
select new
{
fieldName = t.Whatever,
fieldName2 = t.SomeOtherWhatever
};
If I wish to data bind to this class, my column definition would have to include hard-coded strings like "fieldName" and "fieldName2".
Is there any way to call reflection or something else so that I can do something equivelent to the code below (I know the code below is not valid, but am looking for a valid solution).
string columnName = GetPropertyName(myNewData[0].fieldName);
My goal is that if the variable name changes on the anonymous class, a compile-time error would come up until all references were fixed, unlike the current data binding which relies on strings that are not checked until runtime.
Any help would be appreciated.
string columnName = GetPropertyName(() => myNewData[0].fieldName);
// ...
public static string GetPropertyName<T>(Expression<Func<T>> expr)
{
// error checking etc removed for brevity
MemberExpression body = (MemberExpression)expr.Body;
return body.Member.Name;
}
You get your property names like this:
using System.Reflection;
var myNewData = from t in someOtherData
select new
{
fieldName = t.Whatever,
fieldName2 = t.SomeOtherWhatever
};
foreach (PropertyInfo pinfo in myNewData.FirstOrDefault()
.GetType().GetProperties())
{
string name = pinfo.Name;
}
// or if you need all strings in a list just use:
List<string> propertyNames = myNewData.FirstOrDefault()
.GetType().GetProperties().Select(x => x.Name).ToList();