在以前也碰到过EF对象用WCF传输的问题(WCF开发(8) EF),主要是通过DbSet获取的对象,默认情况下不是我们自己定义的类对象,而是动态生成的代理类对象,代理类直接序列化是有问题的,WCF传输过程是需要序列化的。
1.原本打算用
ProxyDataContractResolver的,设置好了,在WPF(.net framework 4.5)中能够获取到,但是在Unity中(Unity2017.3)有问题:
ArgumentException: failed to convert parameters
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:192)
System.Reflection.MonoProperty.SetValue (System.Object obj, System.Object value, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] index, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoProperty.cs:331)
System.Reflection.PropertyInfo.SetValue (System.Object obj, System.Object value, System.Object[] index) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/PropertyInfo.cs:102)
System.Runtime.Serialization.SerializationMap.SetValue (System.Runtime.Serialization.DataMemberInfo dmi, System.Object obj, System.Object value)
Rethrow as InvalidOperationException: Failed to set value of type System.Collections.ArrayList for property Location.WCFServiceReferences.LocationServices.Area[] Areas
System.Runtime.Serialization.SerializationMap.SetValue (System.Runtime.Serialization.DataMemberInfo dmi, System.Object obj, System.Object value)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer, Boolean empty)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.CollectionTypeMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Xml.XmlReader reader, System.Type type, System.Runtime.Serialization.KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, System.String name, System.String ns, Boolean verifyObjectName)
System.Runtime.Serialization.DataContractSerializer.ReadObject (System.Xml.XmlDictionaryReader reader, Boolean verifyObjectName)
System.Runtime.Serialization.XmlObjectSerializer.ReadObject (System.Xml.XmlDictionaryReader reader)
System.ServiceModel.Dispatcher.DataContractMessagesFormatter.MessageToParts (System.ServiceModel.Description.MessageDescription md, System.ServiceModel.Channels.Message message)
System.ServiceModel.Dispatcher.BaseMessagesFormatter.DeserializeReply (System.ServiceModel.Channels.Message message, System.Object[] parameters)
System.ServiceModel.ClientRuntimeChannel.Request (System.ServiceModel.Description.OperationDescription od, System.Object[] parameters)
System.ServiceModel.ClientRuntimeChannel.DoProcess (System.Reflection.MethodBase method, System.String operationName, System.Object[] parameters)
System.ServiceModel.ClientRuntimeChannel.Process (System.Reflection.MethodBase method, System.String operationName, System.Object[] parameters)
怀疑是Unity中的System.ServiceModel的版本(3.0)和WCF服务那边的版本(4.0)不同导致的,WPF做客户端时版本是相同的。
总之new 出来的对象是能传输的,EF查询出来的代理类对象是不能传输的,就算使用
ProxyDataContractResolver,在Unity中也是不行的。
2.采用反射克隆的方式,但是我之前写的函数只能浅克隆,子节点没有克隆进去。
3.采用DataContractSerializer的方式进行克隆,要设置DataContract和DataMember。
Department定位变成
/// <summary>
/// 机构
/// </summary>
[DataContract]
public class Department : ITreeNode<Department>, IDepartment
{
[DataMember]
public int Id { get; set; }
[DataMember]
[Display(Name="机构名")]
public string Name { get; set; }
[DataMember]
[Display(Name = "排序")]
public int ShowOrder { get; set; }
[XmlIgnore]
[Display(Name = "上级机构")]
public virtual Department Parent { get; set; }
[DataMember]
public int? ParentId { get; set; }
[DataMember]
[ForeignKey("ParentId")]
public virtual List<Department> Children { get; set; }
}
从数据库中获取根节点,传给Unity,有问题
public Department GetDepartmentRoot()
{
try
{
Department dep1 = db.Departments.GetRoot();
Department dep2 = dep1.CloneObject();
return dep2;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
public static T CloneObject<T>(this T obj) where T : class,new()
{
return CloneByDataContract(obj);
}
public static T CloneByDataContract<T>(this T obj)
{
var serializer = new DataContractSerializer(typeof(T), new DataContractSerializerSettings()
{
DataContractResolver = new ProxyDataContractResolver()
});
using (var stream = new MemoryStream())
{
// 反序列化
serializer.WriteObject(stream, obj);
stream.Seek(0, SeekOrigin.Begin);
T objNew = (T)serializer.ReadObject(stream);
return objNew;
}
}
ArgumentException: failed to convert parameters
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:192)
System.Reflection.MonoProperty.SetValue (System.Object obj, System.Object value, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] index, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoProperty.cs:331)
System.Reflection.PropertyInfo.SetValue (System.Object obj, System.Object value, System.Object[] index) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/PropertyInfo.cs:102)
System.Runtime.Serialization.SerializationMap.SetValue (System.Runtime.Serialization.DataMemberInfo dmi, System.Object obj, System.Object value)
Rethrow as InvalidOperationException: Failed to set value of type System.Collections.ArrayList for property Location.WCFServiceReferences.LocationServices.Department[] Children
System.Runtime.Serialization.SerializationMap.SetValue (System.Runtime.Serialization.DataMemberInfo dmi, System.Object obj, System.Object value)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer, Boolean empty)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.CollectionTypeMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer, Boolean empty)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.CollectionTypeMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer, Boolean empty)
System.Runtime.Serialization.SerializationMap.DeserializeContent (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.SerializationMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeByMap (System.Xml.XmlQualifiedName name, System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.DeserializeCore (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Type type, System.Xml.XmlReader reader)
System.Runtime.Serialization.XmlFormatterDeserializer.Deserialize (System.Xml.XmlReader reader, System.Type type, System.Runtime.Serialization.KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, System.String name, System.String ns, Boolean verifyObjectName)
System.Runtime.Serialization.DataContractSerializer.ReadObject (System.Xml.XmlDictionaryReader reader, Boolean verifyObjectName)
System.Runtime.Serialization.XmlObjectSerializer.ReadObject (System.Xml.XmlDictionaryReader reader)
System.ServiceModel.Dispatcher.DataContractMessagesFormatter.MessageToParts (System.ServiceModel.Description.MessageDescription md, System.ServiceModel.Channels.Message message)
System.ServiceModel.Dispatcher.BaseMessagesFormatter.DeserializeReply (System.ServiceModel.Channels.Message message, System.Object[] parameters)
System.ServiceModel.ClientRuntimeChannel.Request (System.ServiceModel.Description.OperationDescription od, System.Object[] parameters)
System.ServiceModel.ClientRuntimeChannel.DoProcess (System.Reflection.MethodBase method, System.String operationName, System.Object[] parameters)
System.ServiceModel.ClientRuntimeChannel.Process (System.Reflection.MethodBase method, System.String operationName, System.Object[] parameters)
Children上面的DataMember去掉,就没问题了。
但是new出来的对象就没问题
public Department GetDepartmentRoot()
{
try
{
Department dep3 = new Department() { Id = 1, Name = "Root1" };
Department dep4 = new Department() { Id= 2,Name = "Child1", ParentId = 1 };
dep3.Children = new List<Department>();
dep3.Children.Add(dep4);
Department dep5 = new Department() { Name = "Child112", ParentId = 2 };
dep4.Children = new List<Department>();
dep4.Children.Add(dep5);
string xml3 = dep3.GetXmlText();
return dep3;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
如果是不能传递Children的话,应该都不行的,也就是不是类结构的问题。
经过实验,发现是Children为数量空就不行,null可以,有子节点也可以。
public Department GetDepartmentRoot()
{
try
{
IList<Department> deps = db.Departments.ToList();
foreach (Department dep in deps)
{
if (dep.Children.Count == 0)
{
dep.Children = null;
}
}
return deps[0];
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
这样就行了。
突然发现,奇怪,
不用Clone传输的对象也没问题啊。
是DataContract属性加的的关系吗?
试了试没加DataContract属性,也可以传输。
代理类没有禁用,GetType()类型确实是代理类。
注释掉服务宿主的
host.SetProxyDataContractResolver();
不行,那就是
ProxyDataContractResolver起作用了,但是一开始确实有问题,一开始时我是所有的EF类都没有加上DataContract...难道一个加上了就其作用了?
奇怪。这个先放着,总之不用每个服务代码中都加上Clone了。
继续Children的实验。
因为客户端这边的Children是数组,考虑将服务端也改成数组,结果所有的Department的Children都是null了,EF中关联表的集合对象只能用List,IList吗?
暂时先用Children=null处理吧,现在我对于EF和WCF的知识都还不够,后续有了新知识后再改。
搜索
Rethrow as InvalidOperationException: Failed to set value of type System.Collections.ArrayList for property Location.WCFServiceReferences.LocationServices.Department[] Children
https://answers.unity.com/questions/270072/failure-to-convert-parameter-when-serializing.html
这个的问题和我的一样,然后他的处理方式也是不传Empty,传null。
今天比较晚了,后续再研究吧。
--------------------------------------------------------
回头一看第一点,根本和版本无关,就是空数组的问题Failed to set value of type System.Collections.ArrayList