简体   繁体   中英

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.

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 });

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

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>() , ie

// 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> .

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.

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM