Extension method to get list of dictionaries as dataset?
I'm trying to cast a List of Dictionary objects to a dataset. The List comes from a JSON parser. I decided to use this as an opportunity to learn about extension methods.
The extension method for a single dictionary works, but the method for a List of Dictionaries doesn't "look" right to me, mainly because the call becomes
DataSet myExampleDataSet = myExampleDictionary.ToDataSet<Dictionary<string,string>,string,string>();
Am I missing something? Does it really have to be this complicated? Should I just throw the Dictionary .ToDataSet method in a foreach?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Collections;
//fixed code below
namespace TT.Utils
{
public static class DictionaryExtensions
{
/// <summary>
/// Dictionary to DataSet
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="currentDictionary"></param>
/// <returns></returns>
public static DataSet ToDataSet<TKey, TValue>(this IDictionary<TKey, TValue> currentDictionary)
{
DataSet exportedDataSet = new DataSet();
DataTable exportedDataTable = exportedDataSet.Tables.Add();
foreach (TKey key in currentDictionary.Keys)
{
exportedDataTable.Columns.Add(key.ToString());
}
DataRow newRow = exportedDataTable.NewRow();
foreach (KeyValuePair<TKey, TValue> entry in currentDictionary)
{
string key = entry.Key.ToString();
string val = string.Empty;
if (entry.Value != null)
{
val = entry.Value.ToString();
}
newRow[key] = val;
}
exportedDataSet.Tables[0].Rows.Add(newRow);
return exportedDataSet;
}
/// <summary>
/// List of dictionaries to dataset
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="currentList"></pa开发者_运维百科ram>
/// <returns></returns>
public static DataSet ToDataSet<TKey,TValue>(this IList<Dictionary<TKey,TValue> currentList)
{
DataSet exportedDataSet = new DataSet();
DataTable exportedDataTable = exportedDataSet.Tables.Add();
foreach (Dictionary<TKey, TValue> currentDictionary in currentList.Cast<Dictionary<TKey,TValue>>())
{
foreach (TKey key in currentDictionary.Keys)
{
if (!exportedDataTable.Columns.Contains(key.ToString()))
exportedDataTable.Columns.Add(key.ToString());
}
DataRow newRow = exportedDataTable.NewRow();
foreach (KeyValuePair<TKey, TValue> entry in currentDictionary)
{
string key = entry.Key.ToString();
string val = string.Empty;
if (entry.Value != null)
{
val = entry.Value.ToString();
}
newRow[key] = val;
}
exportedDataSet.Tables[0].Rows.Add(newRow);
}
return exportedDataSet;
}
}
}
You generally don't need to explicitly define the generic type arguments used when calling a generic method. The types will be implied by the types of the arguments you are calling with. If there's an ambiguity, the compiler will let you know.
e.g.,
IDictionary<string, string> myExampleDictionary = ...;
DataSet myExampleDataSet = myExampleDictionary.ToDataSet();
// equivalent to: myExampleDictionary.ToDataSet<string, string>();
IDictionary<string, int> anotherDictionary = ...;
DataSet anotherDataSet = anotherDictionary.ToDataSet();
// equivalent to: anotherDictionary.ToDataSet<string, int>();
Oops, I didn't realize you overloaded ToDataSet()
. I only saw the first one which took a single IDictionary<TKey, TValue>
.
But either way, the second overload should have the generic parameters the same as the first. It will be a IList
of IDictionary
s so it should be in this form:
public static DataSet ToDataSet<TKey, TValue>(this IList<IDictionary<TKey, TValue>> currentList)
{
// ...
}
There are only 2 unknown types here, TKey
and TValue
and that will be implied as usual.
IList<IDictionary<string, string>> myList = ...;
DataSet myDataSet = myList.ToDataSet();
// equivalent to: myList.ToDataSet<string, string>();
IDictionary<string, int> anotherList = ...;
DataSet anotherDataSet = anotherList.ToDataSet();
// equivalent to: anotherList.ToDataSet<string, int>();
Your method call needs not specify the types, which would be:
IDictionary<string, int> test = new Dictionary<string, int>();
// later on.
test.ToDataSet(); // is already valid
Jeff M is right about the redundancy of specifying the generic type-arguments for your first method.
Unfortunately, the signature of the second method is such that it would not be possible for the compiler to infer the type arguments. One option would be to change the signature to:
public static DataSet ToDataSet<TKey,TValue>
(this IList<Dictionary<TKey,TValue>> currentList)
and to remove the redundant call to Enumerable.Cast
.
This will have 2 benefits:
- You wan't have to explicitly specify the generic type arguments, the type-inferencer will figure it out.
- Since no casting is involved, the operation becomes type-safe, so you won't be able to pass a
List<int>
as an argument.
With these two changes, you can do:
Dictionary<string, int> dict = ...
DataSet dictDs = dict.ToDataSet();
List<Dictionary<long, object>> list = ...
DataSet listDs = list.ToDataSet();
精彩评论