Вы можете использовать следующий T4 скрипт.Он управляет IList, IDictionary, типом Nullable и скалярным типом.
Предполагается, что у вас есть этот класс модели ответов, возвращаемый веб-API ASP.NET:

Запустив сценарий T4 (определенный в вашем прокси-проекте), вы получите следующее определение:

Определитьэтот T4 скрипт в вашем прокси-проекте:
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output encoding="utf-8" extension=".cs"#>
<#@ assembly name="$(SolutionDir)xxx.Dis.Services.Endpoints\\bin\\Debug\\xxx.Dis.Services.Endpoints.dll" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="xxx.Dis.Services.Endpoints" #>
<# WriteLine("// ----------------------------------------------------------------------------------------------------------------");
WriteLine("// <auto-generated>");
WriteLine("// This code is automatically generated by tool on " + DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") + ".");
WriteLine("// Values retrieved from project xxx.Dis.Services.Endpoints");
WriteLine("//");
WriteLine("// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.");
WriteLine("// </auto-generated>");
WriteLine("// ----------------------------------------------------------------------------------------------------------------");
WriteLine("using System;");
WriteLine("using System.Collections.Generic;");
WriteLine("using System.Linq;");
WriteLine("using System.Text;");
WriteLine("");
WriteLine("namespace xxx.Dis.Services.Api.ViewModels");
WriteLine("{");
PushIndent(" ");
// WebAPI's Enum generation
var enumList = from type in typeof(xxx.Dis.Services.Endpoints.ViewModels.Base.BaseViewModel).Assembly.GetTypes() where type.IsEnum select type;
foreach (var type in enumList)
{
WriteLine("public enum " + type.Name);
WriteLine("{");
PushIndent(" ");
foreach (var field in type.GetFields())
{
if (field.Name.Equals("value__")) continue;
WriteLine(field.Name + " = " + field.GetRawConstantValue() + ",");
}
ClearIndent();
PushIndent(" ");
WriteLine("}");
WriteLine("");
}
WriteLine("");
// WebAPI's response Models generation, filtering for all response model implementing a specific base class (in this case named BaseResponseViewModel)
var modelList = from type in typeof(xxx.Dis.Services.Endpoints.ViewModels.Base.BaseViewModel).Assembly.GetTypes() where typeof(xxx.Dis.Services.Endpoints.ViewModels.Base.BaseResponseViewModel).IsAssignableFrom(type) select type;
foreach (var type in modelList)
{
WriteLine("public class " + type.Name);
WriteLine("{");
PushIndent(" ");
foreach (var propertyInfo in type.GetProperties())
{
string propertyTypeString;
var propertyType = propertyInfo.PropertyType;
if (IsList(propertyType))
{
propertyTypeString = PrintList(propertyType);
}
else if (IsDictionary(propertyType))
{
propertyTypeString = PrintDictionary(propertyType);
}
else
{
propertyTypeString = PrintScalar(propertyType);
}
WriteLine(" public " + propertyTypeString + " " + propertyInfo.Name + " { get; set; }");
}
ClearIndent();
PushIndent(" ");
WriteLine("}");
}
ClearIndent();
WriteLine("}");
#>
<#+
public static string PrintScalar(Type type)
{
if (type.IsGenericType)
{
var genericDefionitionName = type.GetGenericTypeDefinition().Name;
if (!string.IsNullOrEmpty(genericDefionitionName) && genericDefionitionName.Contains("Nullable"))
{
var propertyType = type.GetGenericArguments()[0];
return propertyType.Name + "?";
}
}
return type.Name;
}
public static string PrintList(Type type)
{
var argumentType = type.GetGenericArguments()[0];
if (argumentType.IsGenericType && IsNullable(argumentType) == false)
{
if (IsList(argumentType))
{
return "IEnumerable<" + PrintList(argumentType) + ">";
}
if (IsDictionary(argumentType))
{
return "IEnumerable<" + PrintDictionary(argumentType) + ">";
}
}
if (IsNullable(argumentType))
{
return "IEnumerable<" + argumentType.GenericTypeArguments[0].Name + "?>";
}
return "IEnumerable<" + argumentType.Name + ">";
}
public static string PrintDictionary(Type type)
{
var argumentsTypes = type.GetGenericArguments();
// First argument must be not nullable
if (argumentsTypes[0].IsGenericType || IsNullable(argumentsTypes[0])) throw new NotSupportedException("First argument of IDictionary must be not nullable.");
var key = argumentsTypes[0].Name;
if (!argumentsTypes[1].IsGenericType) return "IDictionary<" + key + ", " + argumentsTypes[1].Name + ">";
if (IsNullable(argumentsTypes[1])) return "IDictionary<" + key + ", " + argumentsTypes[1].GenericTypeArguments[0].Name + "?>";
var innerArgumentType = argumentsTypes[1];
if (IsList(innerArgumentType))
{
return "IDictionary<" + key + ", " + PrintList(innerArgumentType) + ">";
}
if (IsDictionary(innerArgumentType))
{
return "IDictionary<" + key + ", " + PrintDictionary(innerArgumentType) + ">";
}
return IsNullable(innerArgumentType)
? "IDictionary<" + key + ", " + innerArgumentType.GenericTypeArguments[0].Name + "?>"
: "IDictionary<" + key + ", " + innerArgumentType.Name + ">";
}
public static bool IsNullable(Type t)
{
var result = t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
return result;
}
public static bool IsList(Type t)
{
var result = t.IsGenericType && (t.GetGenericTypeDefinition().IsAssignableFrom(typeof(IList<>)) || t.GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>)));
return result;
}
public static bool IsDictionary(Type t)
{
var result = t.IsGenericType && (t.GetGenericTypeDefinition().IsAssignableFrom(typeof(IDictionary<,>)) || t.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>)));
return result;
}
#>
Наслаждайтесь!