cs0815
cs0815

Reputation: 17408

generic method and anonymous types

Would it be possible to create code like this:

private static string GetInsertString<TDto>()
{
    var type = typeof(TDto);
    var properties = type.GetProperties().Where(Where);
    var tableName = type.Name;

    return string.Format("INSERT INTO {0} ({1}) VALUES ({2});", 
    tableName, 
    string.Join(",", properties.Select(x => x.Name).ToArray()),
    string.Join(",", properties.Select(x => string.Format("@{0}", x.Name.ToLower())).ToArray()));
}

that works with anonymous types like this:

var point = new { X = 13, Y = 7 };

PS:

Output would be:

INSERT INTO Anonymous (X, Y) values (13, 7)

of course you may want to provide the table name.

Upvotes: 7

Views: 3354

Answers (4)

Alessandro D&#39;Andria
Alessandro D&#39;Andria

Reputation: 8868

Assuming you're using .net 4.0 or above, you can use dynamic and ExpandoObject like this:

private static string GetInsertString(dynamic obj)
{
    var expando = (ExpandoObject)obj;

    return string.Format("INSERT INTO {0} ({1}) VALUES ({2});",
        "tableName",
        string.Join(",", expando.Select(x => x.Key)),
        string.Join(",", expando.Select(x => x.Value is string ? "'" + x.Value + "'" : x.Value.ToString())));
}

And then:

dynamic o = new ExpandoObject();

o.a = 10;
o.b = "hello";

var s = GetInsertString(o);

Now s is INSERT INTO tableName (a,b) VALUES (10,'hello');. This is only a draft you have to do some work to get a correct insert string.

Upvotes: 2

khellang
khellang

Reputation: 18102

You won't be able to specify the type parameter with an anonymous type, but if you pass it an object as a parameter, you can use type inference to get a hold of the type:

private static string GetInsertString<TDto>(TDto dto)
{
    var type = typeof(TDto);
    var propertyNames = type.GetProperties().Where(Where).Select(x => x.Name);

    return string.Format("INSERT INTO {0} ({1}) VALUES ({2});",
        type.Name,
        string.Join(",", propertyNames),
        string.Join(",", propertyNames.Select(x => string.Format("@{0}", x.ToLower())));
}

Then call the method: var insertString = GetInsertString(new { X = 13, Y = 7 });

Upvotes: 4

Marc Gravell
Marc Gravell

Reputation: 1062965

It is very hard to use anonymous types with a ...<T>() method. The main way of doing that involves the by example hack, i.e.

var dummy = new { X = 13, Y = 7 };
Foo(dummy); // for Foo<T>(T obj)

or more succinctly:

Foo(new { X = 13, Y = 7 });

which uses generic type inference to deduce that T here is the anonymous type. Foo<T>(T obj) could either be your actual method, or could be a method that in turn calls GetInsertString<TDto>(), i.e.

// overload that takes an example object as a parameter
private static string GetInsertString<TDto>(TDto example) {
    return GetInsertString<TDto>();
}

You could also probably combine the two:

private static string GetInsertString<TDto>(TDto example = null) {
    .. your code ..
}

and pass the example only when it is necessary.

However, the "by example" approach is brittle and susceptible to breaking. I strongly recommend that you simply define a POCO instead:

public class MyType {
    public int X {get;set;}
    public int Y {get;set;}
}

and use GetInsertString<MyType>.

Upvotes: 3

Maarten
Maarten

Reputation: 22945

You can use anonymous objects with generic methods as long as the compiler can resolve the type. For instance, if it can be resolved from a parameter. Like this:

private static string GetInsertString<TDto>(TDto someObject) {
    var type = typeof(TDto);
    var properties = type.GetProperties();  // Removed .Where(Where) since it didn't compile
    var tableName = type.Name;

    return string.Format(
        "INSERT INTO {0} ({1}) VALUES ({2});", 
        tableName, 
        string.Join(",", properties.Select(x => x.Name).ToArray()),
        string.Join(",", properties.Select(x => string.Format("@{0}", x.Name.ToLower())).ToArray())
    );
}

var point = new { X = 13, Y = 7 };
var insertSql = Test.GetInsertString(point);
// Results in: INSERT INTO <>f__AnonymousType0`2 (X,Y) VALUES (@x,@y);

Upvotes: 0

Related Questions