我正在创建一个WCF Web服务。
我正在尝试根据从数据库返回的信息创建一个Type,使用数据构建Type的List,然后将其发回。
但是,我得到了一个
预计不会输入数据合约名称为“MyDynamicType:http://schemas.datacontract.org/2004/07/”的“MyDynamicType”。考虑使用DataContractResolver或将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。 跟踪查看器中的错误。
研究异常。它建议将类型添加到[KnownTypes(Type)]
。如何使用以这种方式创建的Type来执行此操作?
Type Builder类代码来自 How to dynamically create a class in C#?只做了很小的修改。 (即分离创建类型和实例创建。)
Web服务端点
[OperationContract]
[WebInvoke(Method = "GET",
UriTemplate = "/DB/{Q}")]
List<object> DB(string Q);
Web服务端点代码
public List<object> DB(string Q)
{
List<object> rows = DLL.DB.RunQuery(IQueries.LoadQuery(Q));
return rows;
}
运行查询方法
public static List<object> RunQuery(string query)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandText = query;
connection.Open();
using (SqlDataAdapter da = new SqlDataAdapter(command))
{
DataTable dt = new DataTable();
da.Fill(dt);
Dictionary<string, Type> columns = new Dictionary<string, Type>();
foreach (DataColumn dc in dt.Columns)
{
columns.Add(dc.ColumnName, dc.DataType);
}
Type customType = MyTypeBuilder.CreateNewObject(columns);
List <object> rows = new List<object>();
foreach (DataRow dr in dt.Rows)
{
List<object> row = new List<object>();
foreach (DataColumn col in dt.Columns)
{
object customInstance = MyTypeBuilder.CreateObjectInstance(customType);
PropertyInfo pi = customType.GetProperty(col.ColumnName);
pi.SetValue(customInstance, dr[col]);
row.Add(customInstance);
}
rows.Add(row);
}
return rows;
}
}
}
}
我的类型构建器
public class MyTypeBuilder
{
public static object CreateObjectInstance(Type myType)
{
var myObject = Activator.CreateInstance(myType);
return myObject;
}
public static Type CreateNewObject(Dictionary<string,Type> values)
{
var myType = CompileResultType(values);
return myType;
}
public static Type CompileResultType(Dictionary<string, Type> values)
{
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
// NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
foreach (var field in values)
CreateProperty(tb, field.Key, field.Value);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder()
{
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout |
TypeAttributes.Serializable
, null);
return tb;
}
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
提前致谢。
答案 0 :(得分:0)
也许您可以拥有一个所有动态类型都继承自的基类。然后你可以把基类作为你放在knownTypes装饰器中的基类。
答案 1 :(得分:0)
这是一些简单的代码,可以帮助您入门,
[KnownType(typeof(YourSpecialObject))]
[DataContract]
public abstract class BusinessObjectInfo
{
[DataMember]
public int Id { get; set; }
[DataMember]
public Byte[] Version { get; set; }
}
现在你的特殊对象
[DataContract]
public class YourSpecialObject: BusinessObjectInfo
现在你的WCF应该解析KnownTypes。至于了解这一点的方法,Zombie Explorer将提供有关IOC / WCF / PRISM for .NET的大量信息 以下是WCF相当全面的示例的链接http://www.codeproject.com/Articles/474212/Zombie-Explorer-An-n-tier-application-from-top-to
答案 2 :(得分:0)
我以完全不同且更简单的方式做到这一点。我的解决方案基于以下推理:
MyDynamicType
是根据数据创建的
表结构 - &gt; MyDynamicType
的实例实际上是行。的 - &GT; 强> MyDynamicType
的实例实际上是键/值集合。的 - &GT; 强> MyDynamicType
。使用简单的字典就足够了。此字典中的键将是列的名称,值将对应于单元格(列的值)。据我所知,WCF使用DataContractJsonSerializer
将数据序列化为JSON格式,我非常确定它能正确处理字典。另请阅读this文章。
您还可以使用另一个序列化程序而不是默认序列化程序,例如Newtonsoft Json.Net序列化程序。请参阅this或this问题。
最后,为什么你实际上使用WCF创建像API一样的REST?我认为WebApi更容易使用,除非您有一些特定的理由使用WCF。
答案 3 :(得分:0)
所以不幸的是,即使这里所有人都提供了所有重要信息,我也无法让TypeBuilder正常工作。
但是,我尝试使用TypeBuilder的原因是因为我的数据没有像我希望的那样格式化为JSON。它正在返回
{key:dataname,value:value}
当我真正想要的是
{dataname:value}
使用JSON完成Web服务和其他一些经验。我知道Type会符合这种布局。不幸的是,数据是未知的,可以随时更改,Web服务必须处理(不幸的要求)。
即使我无法让TypeBuilder按照我想要的方式工作。我能够使用Newtonsoft.Json获得所需的结果,并在服务器之前序列化字典中的数据然后尝试为我序列化。
感谢大家的帮助。我学到了一些有趣且令人敬畏的新东西,我可能会在其他项目中使用。
由于