I am attempting to take the results of a Stored Procedure that that contains dynamic columns, different shapes of data, and convert that into an IEnumerable or IQueryable.
In the middleware, I am using EF and since I have never worked with dynamic queries, I am at a loss. I looked at the DataTable.ToSchemaTable() to see if that could convert to anything useful and hit a wall. Recently, I started looking into dynamics, however, I still can't seem to figure out how to convert to IEnumerable.
The clientside needs to have data in the form of {"FieldName":"FieldValue"}, and the result set needs to be pre-processed via an IEnumerable prior to being converted to JSON.
Here is the code I am working with that solved the issue.
public static class DataTableExtensions
{
public static IEnumerable<dynamic> AsDynamicEnumerable(this DataTable table)
{
if (table == null)
{
yield break;
}
foreach (DataRow row in table.Rows)
{
IDictionary<string, object> dRow = new ExpandoObject();
foreach (DataColumn column in table.Columns)
{
var value = row[column.ColumnName];
dRow[column.ColumnName] = Convert.IsDBNull(value) ? null : value;
}
yield return dRow;
}
}
}
Related
How can i add multiple rows in DICOM dataset? Without using another DICOM
dataset like List<DicomDataset>?
dt = dac.ExecuteDataSet(dbCommand).Tables[0];
if (dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
DicomDataset _dataset = new DicomDataset();
_dataset.Add(DicomTag.SOPClassUID, SOPClassUID);
_dataset.Add(DicomTag.SOPInstanceUID, GenerateUid());
_dataset.Add(DicomTag.PatientID, dr["PatientID"].ToString());
_dataset.Add(DicomTag.PatientName, dr["PatientName"].ToString());
_dataset.Add(DicomTag.PatientBirthDate, dr["DOB"].ToString());
_dataset.Add(DicomTag.PatientSex, dr["Sex"].ToString());
_dataset.Add(DicomTag.AccessionNumber, dr["AccessionNumber"].ToString());
_dataset.Add(DicomTag.RequestedProcedureDescription, dr["Procedure_Description"].
ToString());
_dataset.Add(DicomTag.RequestedProcedureID, dr["RequestedProcedureId"].ToString());
_dataset.Add(DicomTag.Modality, dr["modality"].ToString());
}
}
Looking at the internal structure of DicomDataset we can see that when you add multiple items, the following methods are called
public DicomDataset Add<T>(DicomTag tag, params T[] values)
{
return DoAdd(tag, values, false);
}
private DicomDataset DoAdd<T>(DicomTag tag, IList<T> values, bool allowUpdate)
{
[...]
return DoAdd(vr, tag, values, allowUpdate);
}
which eventually results in a call like this
if (typeof(T) == typeof(string))
return DoAdd(new DicomApplicationEntity(tag, values.Cast<string>().ToArray()), allowUpdate);
Which adds DicomApplicationEntity objects with the arrays you are passing. Anyway, when you are trying to add multiple objects with the same tag it will fail, since the internal dictionary can only hold one object per type. Hence I guess, that you can add multiple rows for one tag with
_dataset.Add(DicomTag.PatientID, dt.Rows.OfType<DataRow>().Select(row => row["PatientID"].ToString()).ToArray());
I love the might of the .Dump() extension method in LinqPAD and would like to use it to visualize a list of Dictionary<string,string> as a data grid, where the keys are column names and values are single values respectively.
Basically what I want to achieve is:
instead of (currently getting)
You can get this by turning them into ExpandoObjects:
listOfDictionaries.Select(x => x.ToExpando()).ToList().Dump();
public static ExpandoObject ToExpando(this IDictionary<string, string> dict)
{
var expando = new ExpandoObject();
var expandoDic = (IDictionary<string, object>)expando;
foreach (var kvp in dict)
expandoDic.Add(kvp.Key, kvp.Value);
return expando;
}
You could create a DataTable which should display in both modes correctly.
With an extension method like
public static DataTable ToDataTable<T>(this IEnumerable<Dictionary<string,T>> source)
{
DataTable table = new DataTable();
foreach(var dict in source)
{
var dr = table.NewRow();
foreach(var entry in dict)
{
if (!table.Columns.Contains(entry.Key))
table.Columns.Add(entry.Key, typeof(T));
dr[entry.Key] = entry.Value;
}
table.Rows.Add(dr);
}
return table;
}
you can then do something like
listOfDictionaries.ToDataTable().Dump();
how about just
listOfDictionaries.Select(d => new { One = d["one"], Two = d["two"] })
I found the correct way to influence the column names: according to LinqFAQ one has to implement LINQPad.ICustomMembershipProvider.
For Dictionary<string,string> with Keys being column names and Values actual values one just has to add the following code to My Extesions:
public class KVEntry : Dictionary<string,string>, LINQPad.ICustomMemberProvider
{
IEnumerable<string> ICustomMemberProvider.GetNames()
{
return Keys;
}
IEnumerable<Type> ICustomMemberProvider.GetTypes()
{
return Enumerable
.Repeat(typeof(string),Count);
}
IEnumerable<object> ICustomMemberProvider.GetValues()
{
return Values;
}
public KVEntry(Dictionary<string,string> data) : base(data){}
}
Now one has to use KVEntry instead of Dictionary<string,string> in LINQPad queries. This allows me to correctly render my objects and the grid can even be exported to Excel.
Unfortunately, this doesn't work for Results to Data Grids mode, where LINQPad (probably by design) just ignores the ICustomMemberProvider altogether.
I have an MVC application where I need to be able to display various records in a view. For this, I went the route of using the built in Webgrid control. The problem I faced was taking my data from my Datatable returned from the database and converting it into an IEnumerable for the grid. I came across a method on SO to convert it to a type of List Dynamic which seemed to work well enough, but encountered issues when displaying more than about 6 columns of data:
public static dynamic serializeToDynamic(DataTable dt)
{
var result = new List<dynamic>();
foreach (System.Data.DataRow row in dt.Rows)
{
var obj = (IDictionary<string, object>)new System.Dynamic.ExpandoObject();
foreach (System.Data.DataColumn col in dt.Columns)
{
obj.Add(col.ColumnName, row[col.ColumnName]);
}
result.Add(obj);
}
return result;
}
I have a grid that needs to display 28 columns and using this method is extremely slow, with the page taking close to a minute to load. I was unsuccessful in finding any alternatives, so I went ahead and made a model for this information and bound the DataTable to this model which proved to be much faster loading in about 2 seconds. I'd rather not have to use strongly typed models just to display this data, my question is is there any other method to convert a DataTable to work with a webgrid?
If all you need is an IEnumerable of any type then you can use the DataTableExtensions:
var result = dt.AsEnumerable();
That would give you an IEnumerable<DataRow>, which still doesn't carry the benefits of strongly typed objects but is at least an IEnumerable.
This question is seemingly a duplicate of asp.net mvc 3 webgrid bound to List<dynamic> is exceedingly slow which I have just answered.
This ends up being pretty straight-forward. Notice the With extension function.
#model DataTable
#{
var columns = Model.Columns.Cast<DataColumn>().Select(c => c.ColumnName);
var s = Model.Rows.Cast<DataRow>().Select(r => new System.Dynamic.ExpandoObject().With(columns.ToDictionary(c => c, c => r[c])));
WebGrid grid = new WebGrid(s, rowsPerPage: 10);
}
Since ExpandoObject is a dictionary, you can use this extension function:
public static object With(this IDictionary<string, object> obj, IDictionary<string,object> additionalProperties)
{
foreach (var name in additionalProperties.Keys)
obj[name] = additionalProperties[name];
return obj;
}
So, I have an application which lies on a database. So far, the results of my queries all went into a DataTable object like this:
DataTable data = new DataTable();
data.Load(someQuery.ExecuteReader());
Now, I want to load my data into a list of a strongly typed objects. Something like this:
List<MyClass> data = someQuery.Load<MyClass>();
However, my first take on writing that method ended up running almost three times slower than DataTable.Load(IDataReader) method. Basically, I have user GetConstructor(null).Invoke(null) to create and object and I have used PropertyInfo.SetValue(reader.GetValue()) to fill it with data.
Is there a better way to do this?
The method used:
public List<T> LoadData<T>(DbCommand query)
{
Type t = typeof(T);
List<T> list = new List<T>();
using (IDataReader reader = query.ExecuteReader())
{
while (reader.Read())
{
T newObject = (T)t.GetConstructor(null).Invoke(null);
for (int ct = 0; ct < reader.FieldCount; ct++)
{
PropertyInfo prop = t.GetProperty(reader.GetName(ct));
if (prop != null)
prop.SetValue(newObject, reader.GetValue(ct), null);
}
list.Add(newObject);
}
}
return list;
}
To do this efficiently requires metaprogramming. You can use libraries to help. For example, "FastMember" includes a TypeAccessor which provides fast access to instance creation and member-access by name. However, this example is also basically exactly how "dapper" works, so you could just use dapper:
int id = ...
var data = connection.Query<Order>(
"select * from Orders where CustomerId = #id",
new { id }).ToList();
You can also open up the "dapper" code to see what it does.
You can execute your query using linQ and get the Generic List and then if you want to conver it to DataTable then use the following Code, it may help you.
public DataTable ListToDataTable<T>(IEnumerable<T> list)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in list)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
it will work for any strongly type class. Please check the time it takes to execute.
Thanks,
I'm migrating an older .net application to .net 4, this migration has to be done in several stages, thats why some of the methods might seem a bit unconventional. Anyway...
What I have is a Stored Procedure (Analysis_select) returning one row with several columns with the result. If i call it with
var result = dbContext.Analysis_select(user.UserId, Year, Week);
everything is fine, i can view the data in with the debugger or display it in a grid view or something like that, so the expression and Stored Procedure really works! But the result is not compatible with the rest of the code so...
If I try to cast it to DataSet it fails, Visual Studio actually sais this is ok but when rendering on a web page it crashes
var result = (DataSet)dbContext.Analysis_select(user.UserId, Year, Week);
The error is as follows
Unable to cast object of type 'SingleResult`1[Analysis_select]' to type 'System.Data.DataSet'.
I've read about some other conversions from linq to DataSet but most of the methods seems a bit excessive for this. The reason why I want to keep the DataSet is that there's tens of thousands of lines of code depending on such results. Sucks yes, but can you help me fix this?
Any help is highly appreciated, thanks!
I'm not suggesting this as a great solution or best practices; there is most definitely a different (and probably better) way.
For a case where you have IEnumerable and no other means to create a data table, reflection can step in.
You could use something like below...
public static class ExtensionMethods
{
public static DataTable ToDataTable<T>(this IEnumerable<T> items)
{
DataTable table = new DataTable();
var properties = typeof(T).GetProperties();
foreach (var propertyInfo in properties)
{
table.Columns.Add(propertyInfo.Name, typeof(object));
}
foreach (var item in items)
{
var row = properties.Select(p => NormalizeObject(p.GetValue(item, null))).ToArray();
table.Rows.Add(row);
}
return table;
}
private static object NormalizeObject(object value)
{
Binary bin = value as Binary;
if (bin != null)
{
return bin.ToArray();
}
XElement element = value as XElement;
if (element != null)
{
return element.ToString();
}
return value;
}
}
You will need to write an extension method to convert the IEnumerable into a DataSet. Here is an example of how to convert IEnumerable to a DataTable.
private DataTable ToDataTable<T>(List<T> items)
{
var table = new DataTable(typeof (T).Name);
PropertyInfo[] props = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in props)
{
Type t = GetCoreType(prop.PropertyType);
table.Columns.Add(prop.Name, t);
}
foreach (T item in items)
{
var values = new object[props.Length];
for (int i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
table.Rows.Add(values);
}
return table;
}
public static Type GetCoreType(Type t)
{
if (t != null && IsNullable(t))
{
if (!t.IsValueType)
{
return t;
}
else
{
return Nullable.GetUnderlyingType(t);
}
}
else
{
return t;
}
}
public static bool IsNullable(Type t)
{
return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
Here's a link to the source of this solution: http://www.chinhdo.com/20090402/convert-list-to-datatable/
did you check this tutorial http://msdn.microsoft.com/en-us/library/bb386921.aspx from MS? Otherwise there is no direct conversion between LINQ result and Dataset.
With LINQ2SQL stored procedures you never get DataSets. What you get is Exactly that, a SingleResult. Is an IEnumerable.