tags:

views:

142

answers:

4

Hi,

I'm implementing "type-independent" method for filling DataRow with values. Intended functionality is quite straight forward - caller passes the collection of string representations of column values and DataRow that needs to be filled:

private void FillDataRow(List<ColumnTypeStringRep> rowInput, DataRow row)

ColumnTypeStringRep structure contains the string representation of value, column name, and - what's of most importance - column data type:

private struct ColumnTypeStringRep
{
    public string columnName; public Type type; public string stringRep;
    public ColumnTypeStrinRep(string columnName, Type type, string stringRep)
    {
        this.columnName = columnName; this.type = type; this.stringRep = stringRep;
    }
}

So what's that "type-independency"? Basically I don't care about the data row schema (which always be a row of some typed data table), as long as passed column names match DataRow's colum names and column data types match those of DataRow I'm fine. This function needs to be as flexible as possible (and as simple as possible - just not simpler). Here it is (almost):

private void FillDataRow(List<ColumnTypeStrinRep> rowInput, DataRow row)
{
Debug.Assert(rowInput.Count == row.Table.Columns.Count);

foreach (ColumnTypeStrinRep columnInput in rowInput)
{
    Debug.Assert(row.Table.Columns.Contains(columnInput.columnName));
    Debug.Assert(row.Table.Columns[columnInput.columnName].DataType == columnInput.type);

    // --> Now I want something more or less like below (of course the syntax is wrong) :

    /*
    row[columnInput.columnName] =  columnInput.type.Parse(columnInput.stringRep);
    */

    // --> definitely not like below (this of course would work) :

    /*
    switch (columnInput.type.ToString())
    {
        case "System.String":
            row[columnInput.columnName] = columnInput.stringRep;
            break;
        case "System.Single":
            row[columnInput.columnName] = System.Single.Parse(columnInput.stringRep);
            break;
        case "System.DateTime":
            row[columnInput.columnName] = System.DateTime.Parse(columnInput.stringRep);
            break;
        //...
        default:
            break;
    }
    */
}

}

Now You probably see my problem - I don't want to use the switch statement. It would be perfect, as in the first commented segment, to somehow use the provided column type to invoke Parse method of particular type that returns the object of that type constructed from string representation. The switch solutions works but it's extremely non flexible - what if in future I'll be filling not the DataRow but some other custom type with "columns" that can be of custom type (of course every such type will need to expose Parse method to build itself from string representation).

Hope you got what I mean - its like "dynamic parsing" kind of functionality. Is there a way to achieve this in .NET?

Example of FillDataRow call could look like this:

List<ColumnTypeStrinRep> rowInput = new List<ColumnTypeStrinRep>();
rowInput.Add(new ColumnTypeStringRep("colName1", Type.GetType("System.Int32"), "0"));
rowInput.Add(new ColumnTypeStringRep("colName2", Type.GetType("System.Double"), "1,11"));
rowInput.Add(new ColumnTypeStringRep("colName3", Type.GetType("System.Decimal"), "2,22"));
rowInput.Add(new ColumnTypeStringRep("colName4", Type.GetType("System.String"), "test"));
rowInput.Add(new ColumnTypeStringRep("colName5", Type.GetType("System.DateTime"), "2010-01-01"));
rowInput.Add(new ColumnTypeStringRep("colName6", Type.GetType("System.Single"), "3,33"));

TestDataSet.TestTableRow newRow = this.testDataSet.TestTable.NewTestTableRow();
FillDataRow(rowInput, newRow);
this.testDataSet.TestTable.AddTestTableRow(newRow);
this.testDataSet.TestTable.AcceptChanges();

Thank You!

A: 

First off, your using a struct to pass a complex data around. That is a really bad idea. Make that a class instead.

That said, it sounds like you need a factory to create instances of a parser interface:

interface IColumnTypeParser
{
    // A stateles Parse method that takes input and returns output
    DataColumn Parse(string input);
}

class ColumnTyeParserFactory
{
    IColumnTypeParser GetParser(Type columnType)
    {
        // Implementation can be anything you want...I would recommend supporting
        // a configuration file that maps types to parsers, and use pooling or caching
        // so you are not constantly recreating instances of your parsers (make sure your
        // parser implementations are stateless to support caching/pooling and reuse)

        // Dummy implementation:
        if (columnType == typeof(string)) return new StringColumnTypeParser();
        if (columnType == typeof(float)) return new FloatColumnTypeParser();
        // ...
    }
}

Your FillDataRow implementation would hten use the factory:

m_columnTypeParserFactory = new ColumnTypeParserFactory();

private void FillDataRow(List<ColumnTypeStrinRep> rowInput, DataRow row)
{
    foreach (ColumnTypeStrinRep columnInput in rowInput)
    {
        var parser = m_columnTypeParserFactory.GetParser(columnInput.type);
        row[columnInput.columnName] parser.Parse(columnInput.stringRep);
    }
}
jrista
I agree on the structure, but there is no need to reinvent type conversion.
Daniel Brückner
Thanks for the structure hint! Your solution is nice design patterns re-freshener but that's not what I was looking for - it's only moving the switch statement from one place to another (into ColumnTypeParserFactory). I was thinking about something more ".NET automagicall" to play with types (reflection?). Thanks anyway!
indigo
@Daniel: Ah, your right! I haven't used the .NET TypeConversion stuff for a while, but thats a much better solution. That leaves my answer as mostly an academic study in the Factory Pattern. ;)
jrista
@Indigo: Daniel's answer is actually a lot better. I had forgotten about .NET's built-in type conversion capabilities. Thats a much better solution, although you would still need to implement type converters for custom types (which essentially correspond to my interface).
jrista
@Indigo: FYI about the switch statement...if you read the comment before that, you'll notice I recommended a configured approach with caching of the parser instances. That would not only eliminate the switch, but also allow you to plug in new converters dynamically via configuration, without requiring recompilation.
jrista
ahh sorry - indeed I forgot about the comment. But thats another moving of a switch this time into configuration file :) So no winner here (point for solution that doesn't require recompilation however)
indigo
A: 

In short there is nothing like that - no automagical conversion. You have to specify conversion yourselves. That said

static class Converters{
    static Dictionary<Type, Func<string, object>> Converters = new ...

    static Converters{
        Converters.Add(typeof(string), input => input);
        Converters.Add(typeof(int), input => int.Parse(input));
        Converters.Add(typeof(double), double => double.Parse(input));
    }
}

void FillDataRow(IList<string> rowInput, row){
    for(int i = 0; i < rowInput.Length; i++){
       var converter = Converters.Converters[Column[i].DataType];
       row[i] = converter(rowInput[i])
    }
}
That's just not right - the framework provides several choices for extensible type conversion.
Daniel Brückner
Thats similar to jrista solution (or more accurate his solution is similar to yours judging by answered time) - however Im looking for solution where I don't need to provide type-by-type what to do - no matter if it is jrista's if-ing or yours lambda-ing. However yours solutions are probably far more faster than potential ones using reflection-kind-of-magic I'm looking for. Thanks anyway!
indigo
+2  A: 

The TypeConverterclass is the generic .NET way for converting types. The System.ComponentModel namespace includes implementation for primitive types and WPF ships with some more (but I am not sure in which namespace). Further there is the static Convert class offering primitive type conversions, too. It handles some simple conversions on itself and falls back to IConvertible.

Daniel Brückner
Thank You - i will check it out soon. Looks promising. I was also thinking about "pure" reflection - using GetMethod("Parse") for particular type then invoking Parse through MethodInfo - I'm not sure if it's the right way and I cannot make it to work.
indigo
Great answer. Forgot about .NET TypeConverters.
jrista
A little addendum on the Convert class. You should be able to use the Convert.ChangeType() method for converting everything, not just primitives. So long as you have a TypeConverter, the ChangeType method should find your type converters and perform conversion so long as the source and destination types are supported by some converter somewhere in the available assemblies.
jrista
You have to link your custom types with the TypeConverter attribute to specific type converter implementations. After that the Convert class is able to find the needed type converters.
Daniel Brückner
Ok I quickly checked out those TypeConverters - looks like we have a winner here! Thanks again Daniel!
indigo
A: 

How about Convert.ChangeType?
You might consider something fancy with generics btw.

foreach (ColumnTypeStrinRep columnInput in rowInput)
{
    Debug.Assert(row.Table.Columns.Contains(columnInput.columnName));
    Debug.Assert(row.Table.Columns[columnInput.columnName].DataType == columnInput.type);
    ...
    row[columnInput.columnName] = Convert.ChangeType(columnInput.stringRep, columnInput.type);
}

More on Convert.ChangeType:
http://msdn.microsoft.com/en-us/library/dtb69x08.aspx

Zyphrax