Blog Archives
Parse XML to Dynamic ExpandoObject C# .NET 4.0
Posted by Ronnie Diaz
The example below uses the LINQ to XML predecessor System.Xml.XmlDocument simply because I still prefer the regex parsing method. It can easily be adapted to use XDocument instead based on your preference. The end resulting output (IEnumerable) is the same.
code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Dynamic; using System.Xml.Linq; namespace AIS.Common { public class DocParser : IDisposable { public enum DocTypes { Xml }; private XmlDocument Document { get; set; } //private XDocument Document { get; set; } private DocTypes DocType { get; set; } //constructor /// <summary> /// /// </summary> /// <param name="dt">type of document, currently only supports xml</param> /// <param name="f">filename and path</param> public DocParser(DocTypes dt, string fp) { try { switch (dt) { case DocTypes.Xml: DocType = DocTypes.Xml; Document = new XmlDocument(); Document.Load(fp); //Document = XDocument.Load(fp); break; default: throw new Exception("Constructor::invalid or unknown doc type specified"); } } catch (Exception ex) { throw new Exception("Constructor::Error with document load or doc type.",ex); } } public IEnumerable<dynamic> GetElements(string regex) { return XmlToExpandoObject(regex); } private IEnumerable<dynamic> XmlToExpandoObject(string regex) { dynamic eo = new ExpandoObject(); var dictionary = eo as IDictionary<string, object>; XmlNodeList xnl = Document.SelectNodes(regex); foreach (XmlNode xn in xnl) { foreach (XmlAttribute xa in xn.Attributes) { dictionary[xa.Name.ToString()] = xa.Value.Trim(); //TrySetAttr(xn.LocalName, xn); } yield return eo; } } } }
sample xml:
<?xml version="1.0" encoding="utf-8" ?> <robots> <robot id="1" age="5" /> <robot id="2" age="3" /> </people>
usage:
DocParser dp = new DocParser(DocParser.DocTypes.Xml, Server.MapPath("~/xmlfilename.xml")); //can also retrieve xml from external service, etc var robots = dp.GetElements("*/robot"); foreach (var r in robots) { Console.WriteLine("Robot id={0}, age={1}", r.id,r.age); //output. if this is web app you will likely do this differently }
References
http://msdn.microsoft.com/en-us/magazine/ff796227.aspx
Multiple Stored Procedures with same return type in LINQ
Posted by Ronnie Diaz
Previously you may have achieved this functionality using datareader. Now that you are using LINQ, you may be wondering how to achieve the same with lists of strongly typed objects.
Quite a few articles out there have reproduced DataReader functionality with LINQ. This article suggests a new approach using the SqlDataSource to create an almost identical code pattern as you previously used with DataReader.
So instead of directly recreating DataReader using LINQ, I’ve replaced it with the same code pattern using a different base class altogether.
old datareader code pattern:
set reader
set connection
set reader parameters
reader loop
get/set reader data
end reader loop
new sqldatasource code pattern:
set sqldatasource
set connection
set sqldatasource parameters
lambda foreach loop calling external delegate function for each item in result
The new approach encapsulates the loop within a LINQ lambda ForEach statement that gets/sets the values and loops through each item inherently. (sourcecode examples below)
Why the hassle?
Sometimes it is useful to load a list of objects from a stored procedure, view or table without immediately binding it to a grid or other data control. The old .NET 2.0 datareader offered a means to easily do this while allowing you the flexibility to do additional work within the reader loop so I sought to reproduce this.
You may be surprised to find the below example does not utilize the LINQ dbml assistance you would normally use in a LINQ to SQL scenario. There is good reason for this as I would like to create a function that allows full control over the list of return values without auto-generating a strange new hybrid return type for every stored procedure.
In addition, I like to fully prototype my applications prior to linking them to the database (that’s how you know you’ve been coding a long time) and this approach makes this much easier.
Simple example:
public class Person { public int Id {get;set;} public string Name {get;set;} public DateTime DateOfBirth {get;set;} public decimal Age {get;set;} //this value isn't actually stored in db and is calculated on load } public static List<Person> LoadPersonsByFilter(string Filter, char Country) { SqlDataSource sds = new SqlDataSource(); sds.ConnectionString = ConfigValues.ConnectionString; //encapsulates ConfigurationManager.ConnectionStrings sds.SelectCommandType = SqlDataSourceCommandType.StoredProcedure; sds.SelectCommand = "get_persons"; sds.SelectParameters.Add("Filter", Filter); sds.SelectParameters.Add("CountryID", Country.ToString()); //sds.Selecting += new SqlDataSourceSelectingEventHandler(sds_Selecting); List<TrainingEvent> results = new List<TrainingEvent>(); sds.Select(new DataSourceSelectArguments()).Cast<DataRowView>().ToList().ForEach(o => LoadPersons_AddResult(o, ref results)); return results; } private static void LoadPersons_AddResult(dynamic o, ref List<Person> results) { results.Add(new Person { Id = Convert.ToInt32(o["PersonID"]), Name = Convert.ToString(o["Name"]), DateOfBirth = Convert.ToDateTime(o["DateOfBirth"]), Age = CalcAge(Convert.ToDateTime(o["DateOfBirth"])) }); } public decimal CalcAge(DateTime DOB) { TimeSpan ts = DateTime.Now-DOB; return Convert.ToDecimal(ts.TotalDays/365); }
At this point, if you are familiar with Linq, you may be thinking of how the same can be accomplished without much effort by simply returning “new” within your linq query. (PM me if you are unsure what I mean by this).
This is true, you could do this, but as complexity increases, you quickly have to look to other alternatives or your LINQ will increase in complexity and create more room for error.
Consider the following strongly typed example:
public List<Computer> Computers {get;set;} //the memory module, cpu and harddrive classes should be self explanatory public class Computer { public int Id {get;set;} public List<MemoryModule> MemoryModules {get;set;} public List<CPU> CPUs {get;set;} public List<HardDrive> HardDrives {get;set;} } public static List<Computers> LoadComputersByFilter(string Filter, char Country) { SqlDataSource sds = new SqlDataSource(); sds.ConnectionString = ConfigValues.ConnectionString; //encapsulates ConfigurationManager.ConnectionStrings sds.SelectCommandType = SqlDataSourceCommandType.StoredProcedure; sds.SelectCommand = "get_computers"; sds.SelectParameters.Add("Filter", Filter); sds.SelectParameters.Add("CountryID", Country.ToString()); //sds.Selecting += new SqlDataSourceSelectingEventHandler(sds_Selecting); List<Computers> results = new List<Computers>(); sds.Select(new DataSourceSelectArguments()).Cast<DataRowView>().ToList().ForEach(o => LoadComputersByFilter_AddResult(o, ref results)); //results might look something like //compid,mmid,cpuid,hdid //1,1,1,1 - denotes first stick //1,2,1,1 - denotes second stick //1,1,1,2 - denotes second hd //1,1,2,1 - denotes second cpu //this is just an example, and would be structured slightly different in a production scenario but the concept remains the same //you can now bind each sub list to its own nested repeater, etc return results; } private static void LoadPersons_AddResult(dynamic o, ref List<Computer> results) { MemoryModule mm = new MemoryModule() { //load info and determine specs or other complex results etc }; CPU cpu = new CPU() { //load info and determine specs or other complex results etc }; HardDrive hd = new HardDrive () { //load info and determine specs or other complex results etc }; results.Add(new Computer { Id=Convert.ToInt32(o["compid"]), MemoryModule = mm, CPU = cpu, HardDrive = hd }); }
As illustrated by the above example, you can easily nest lists within one another without worrying about having to recode a casting mechanism from the custom return type from autogenerated LINQ dbml, or without having to modify the dbml file directly (which resets on updates btw).
The above can be accomplished using LINQ to SQL and lambda expressions, but it will require more practice on your part and is not as explicit IMO.
You also may be wondering why the database call was made by a single stored procedure rather than three separate calls.. If you are unsure, consider the math. Using 1 call to return 4 rows is faster and less work on the database than using 3 calls to return 4 rows.
I should mention there is an alternative approach of creating a custom class that inherits from IEnumerable and can intercept the lazy loading that occurs, but this actually takes more time in my opinion, and may be more prone to error as there are more steps involved.
Enjoy. 😉
meta: Linq DataReader Load Nested List of Objects and Complex Data Results from SqlDataSource
Posted in Programming & Development
Tags: add result, bind, complex loading, data, databind, datareader, datasource, dynamic, linq, linq datareader, linq to sql, list, list of objects, load, load list, loading complex, loading lists, nested, nested list, nested lists, result, sql, sqldatasource, stored procedure, strongly type, type
c# .net list anonymous read only store dynamic types in session
Posted by Ronnie Diaz
If you’re new to .Net 4.0 “dynamic” type, then you may or may not be surprised to find this is reserved for unique object types with read-only properties, as compared to its previous counterpart the “object” type.
In general, this is not an issue that many will encounter, at least initially, since many implementations will probably be focused on one-way databinding to controls on pages which in turn update the database directly instead of the in-memory object storing the values from the initial data results.
However, keep in mind that as cool as dynamic is, this immutable nature makes for a mess in circumstances that require two-way binding, such as with a session variable (example below). If you are keen on using it in these scenarios, never fear, I have worked up your solution. 😉
A typical practice sometimes utilized for Session variables is to use a common class for getting/setting session values, such as (you can skip ahead if you’re familiar with this aspect):
The immutable Session scenario
public static class SessionVars { public static object MySessionValue{get {return getval("MySessionValue");} set {setval("MySessionValue",value);}} public static List<object> MyListOfSomething { get { return (List<object>)getval("MyListOfSomething"); } set { setval("MyListOfSomething",value); } } private static object getval(string key) { try { return HttpContext.Current.Session[key]; } catch (Exception ex) { //YourErrorClass.HandleError(ex); //return "Error retrieving value"; return null; } } private static void setval(string key, object value) { try { HttpContext.Current.Session[key] = value; } catch (Exception ex) { //YourErrorClass.HandleError(ex); //return "Error setting value"; } } }
The above could then be utilized such as:
SessionVars.MyListOfSomething =somevalue; //or SessionVars.MyListOfSomething [0]=somevalue; //or yourdatabindingcontrol.DataSource = SessionVars.MapNames; yourdatabindingcontrol.DataBind(); //etc..
This of course makes Session usage much easier, however, our third line of code in the above example would not be possible if it were of type dynamic, which otherwise generally replaces object.
If MyListOfSomething were instead List of dynamic, you would have to use the following:
Solution
//for list var removei = SessionVars.MyListOfSomething [index]; SessionVars.MyListOfSomething .Remove(removei); string newvalue = "somenewvalue"; var addi = new { id = removei.id, name = removei.name, value= newvalue }; SessionVars.MyListOfSomething .Add(addi); //for single value var removei = SessionVars.MySessionValue; string newvalue = "somenewvalue"; var addi = new { id = removei.id, name = removei.name, value= newvalue }; SessionVars.MySessionValue=addi;
References
MSDN (Immutable anonymous types), c# .net list anonymous read only store dynamic types in session
Store List to Session (StackOverflow), http://stackoverflow.com/questions/1259934/store-list-to-session
OrderBy Column Name as String using Linq C# .Net Dynamic Sorting of Anonymous Types
Posted by Ronnie Diaz
If you’re familiar with Linq, you have undoubtedly used the popular “OrderBy” extension method. Unfortunately, this method does not accept a string value with the column name.
var data = from i in db.tablename select i; repeatername.datasource = data.OrderBy(i=>i.columnname); //this works repeatername.datasource = data.OrderBy("columnname"); //this does not
To resolve, you can add the following small class or just the method to a generic DLL you use in your web application projects or create a new class or project for your extension methods.
Kudos to R. Prestol for help researching this one.
using System.Linq; using System.Linq.Expressions; using System; namespace YourAppName.Web { public static class extensionmethods { public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending) { var param = Expression.Parameter(typeof(T), "p"); var prop = Expression.Property(param, SortField); var exp = Expression.Lambda(prop, param); string method = Ascending ? "OrderBy" : "OrderByDescending"; Type[] types = new Type[] { q.ElementType, exp.Body.Type }; var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); return q.Provider.CreateQuery<T>(mce); } } }
Returning to our first code snippet, you can now do:
repeater.datasource = data.OrderByField("columnname");
Since this is an extension method, and exists in a class within the same namespace as your project or other referenced assembly, you can call the method directly from the IQueryable Linq data object without requiring any other inherits; this works similar to override functionality.
Read up about extension methods on MSDN or Google for other cool tricks you can do. Enjoy. 😉
References
Extension Methods (MSDN), http://msdn.microsoft.com/en-us/library/bb383977.aspx