tags:

views:

165

answers:

4

Hi all,

I've been looking on SO this morning to find a way of creating a List of specific objects from a System.Data.DataTable. So far, I have this in my DomainModel base class:

    protected static List<T> ConvertTo<T>(DataTable dt) where T : class, new()
    {
        int count = dt != null ? dt.Rows.Count : 0;
        List<T> list = new List<T>(count);

        if (dt != null & dt.Rows.Count > 0)
        {
            foreach (DataRow row in dt.Rows)
            {
                T item = new T(); // ????
                FillObjectFromDataRow(item, row);
                list.Add((T)item);
            }
        }

        return list;
    }

Note: I want an empty list returned, as mostly it just gets bound to datagrids, repeaters, etc., mostly.

However, it doesn't work as the classes generally have a private constructor to prevent unauthorised instantiation (I'm mean that way), so I get the "type 'typename' must be a non-abstract type with a public parameterless constructor" error.

I am loathe to introduce a public parameterless constructor to my classes as they contain 90% of static methods (which are used with ObjectDataSources) and having an "empty" class being created would not make sense. I.e. a new Employee object would be blank instead of being created by public static Employee Get(string employeeID), which would contain far more useful information.

Any ideas on how I can create a "thing" of T at the '????' marked row without using the new() constraint? Can I instantiate a "thing" via a Type parm?

Thanks for any ideas, just trying to DRY in my apps by having this...

Kind regards,

Mike K.

PS And yes, maybe the dt.Rows.Count > 0 call is unnecessary...

+1  A: 

You can use Activator.CreateInstance<T>().

The CreateInstance generic method is used by compilers to implement the instantiation of types specified by type parameters.

Dzmitry Huba
No I can't, it doesn't work with objects that have no parm-less constructors - I get a "No parameterless constructor defined for this object" runtime error. :-(
Mike Kingscott
+2  A: 

You can use either Activator.CreateInstance<T>() or invoke the constructor via typeof(T).GetConstructor(...).Invoke(new object[] { ... });

If you need alot of this operations; you can also use Expression.New to create them; as CreateInstance and via the constructor are quite expensive methods.


Example of doing it the Expression based way.

    private void Foo()
    {
        TestJan myObject = CreateObject<TestJan>("Jan", 21);
    }

    private T CreateObject<T>(string name, int age)
    {
        //first get a reference to the ConstructorInfo
        //we know constructor has 2 params, string and int32. Names are not important.
        System.Reflection.ConstructorInfo ci = typeof(T).GetConstructor(new[] { typeof(string), typeof(int) });

        //we now have to define the types
        ParameterExpression stringParam = Expression.Parameter(typeof(string), "stringExp");
        ParameterExpression intParam = Expression.Parameter(typeof(int), "intExp");

        //create an expression
        NewExpression constructor = Expression.New(ci, stringParam, intParam);

        //wrap this in a lambda-expression, which returns basically
        //    (stringExp, intExp) => new T(stringExp, intExp);
        LambdaExpression lambda = Expression.Lambda(constructor, stringParam, intParam);

        //compile into delegate
        var constructorDelegate = (Func<string, int, T>)lambda.Compile();

        //invoke the delegate. Normally you would cache this in a static Dictionary<Type, Delegate>.
        //when you cache this, it's lightning fast. As it's just as fast as hard program
        //    (stringExp, intExp) => new T(stringExp, intExp);
        return constructorDelegate.Invoke(name, age);
    }

You can vary of course with the parameters and such, I find this the way best way to create an object, because it's both fast and flexible.

Jan Jongboom
See my answer to Dzmitry re Activator.CreateInstance - I've not tried GetConstructor but share your concern about expense.
Mike Kingscott
If you really care about the performance. You should create a compiled Expression tree, using `Expression.New`, then wrap it in an `Expression.Lambda` and then `.Compile()` to get a reusable delegate that does it as fast as native code.
Jan Jongboom
@Jan, you big, me small - that's over my head :-s For my edification, could I see some pseudo-code / code for what you describe? It would help me and others, thank you :-)
Mike Kingscott
Check it out :-)
Jan Jongboom
O-kay, thanks for the code. Apart from using it in the context of my original post, how often do you use this way of creating objects? Is it relevant to DI? Is it purely for performance? Gah, I learn something new, and then all I have is more questions ;-)
Mike Kingscott
I use this f.e. for deserializing alot of objects from byte arrays, create API entities from Business entities, etc. etc. Basically everything that can be done through reflection; but where reflection is too slow. You use reflection to create a tree (just like here with the `ConstructorInfo`), and create a compiled delegate, stored in a singleton. Flexible and Fast.
Jan Jongboom
+2  A: 

As mentioned already, the Activator.CreateInstance approach should do the job - the class, new() is a convenience, and expresses what we know should be the case, of course... is there any particular reason you don't want it?

Another approach would be to pass in a factory method - this might help in particular with any DI concerns etc:

protected static List<T> ConvertTo<T>(DataTable dt, Func<T> ctor)
{
    {loop}
        T newItem = ctor();
        {fill row}
    {/loop}
}

You could then call as (for example):

List<Person> people = ConvertTo<Person>(dataTable, () => new Person());

or:

List<Person> people = ConvertTo<Person>(
     dataTable, () => diContainer.Create<Person>()); // for your DI service
Marc Gravell
I don't want other folks to create "blank" objects as they would be worthless, i.e. there are very few (almnst none) operations done with empty classes, see my original post for how I would retrieve a person via a static GetPerson call. (That's not a Bad Thing, is it?)That said, your factory method / erikkallen's creator lambda may be the way to go, I will try it shortly. I did see something similar previous but couldn't get my head around it; seeing it related to my code has helped :-)
Mike Kingscott
Great, that works - reading up on Func<T> has educated me a bit ("Encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter", e.g. a constructor), I'm just a bit hazy on the "() => new Person()" syntax. Stepping through the code in the base class, I see it calls the ctor method which then goes to the sub-class to call the () method which returns a new Person... Ahhh, I think I get it :-)
Mike Kingscott
No, now I really get it - that's great :-) Thanks for your help, hope other people read this thread as it's a complete, working example.
Mike Kingscott
+1  A: 

An alternative to the reflection solution could perhaps be to use a creator lambda:

protected static List<T> ConvertTo<T>(DataTable dt, Func<DataRow, T> create) {
    int count = dt != null ? dt.Rows.Count : 0;
    List<T> list = new List<T>(count);
    if (dt != null & dt.Rows.Count > 0)
    {
        foreach (DataRow row in dt.Rows)
        {
            list.Add(create(row));
        }
    }
    return list;
}

which you then invoke like:

var result = ConvertTo(dt, row => CreateObjectFromRow(row));
erikkallen
Upvoted as it's a similar approach to Marc's, but sorry not marked as answer as calling code can look like this:return ConvertTo<Employee>(_EmployeeDA.Select(companyID), () => new Employee());which does away with the need to call to CreateObjectFromRow on the call; also, it'll be able to be used with DI.
Mike Kingscott
The CreateObjectFromRow call in my answer was meant as a method that constructs the object, perhaps using a static factory method which takes a DataRow as an argument. Or whatever you desire.
erikkallen