在使用EntityFramework5.0,并将其作为WCF放在服务端的时候遇到了如下问题:
1. EF在客户端与服务端之间传输问题 由于将EF放在服务端,所以类必须具有DataContract属性。
服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型。
只有声明为DataContract的类型的对象可以被传送,且只有成员属性会被传递,成员方法不会被传递。WCF对声明为DataContract的类型提供更加细节的控制,可以把一个成员排除在序列化范围以外,也就是说,客户端程序不会获得被排除在外的成员的任何信息,包括定义和数据。默认情况下,所有的成员属性都被排除在外,因此需要把每一个要传送的成员声明为DataMember,如下所示。
1 2 3 4 5 6 7 8 [DataContract ] public partial class base_user { [DataMember ] public string user_code { get ; set ; } [DataMember ] public string user_num { get ; set ; } }
那么如何给EF生成的所有类加入这一属性?在EntityFramework5.0中使用T4模板生成类,同时也能很方便的进行批量修改:
1.进入实体类的T4模板
2.添加引用,修改UsingDirectives
:
1 2 3 4 5 6 7 8 9 10 11 12 public string UsingDirectives (bool inHeader, bool includeCollections = true ){ return inHeader == string .IsNullOrEmpty(_code.VsNamespaceSuggestion()) ? string .Format( CultureInfo.InvariantCulture, "{0}using System;{1}{3}" + "{2}" , inHeader ? Environment.NewLine : "" , includeCollections ? (Environment.NewLine + "using System.Collections.Generic;" ) : "" , inHeader ? "" : Environment.NewLine, Environment.NewLine + "using System.Runtime.Serialization;" ) : "" ; }
3.添加数据契约属性
1 2 3 4 5 6 7 8 9 10 public string EntityClassOpening (EntityType entity ){ return string .Format( CultureInfo.InvariantCulture, "{4}{0} {1}partial class {2}{3}" , Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore(" : " , _typeMapper.GetTypeName(entity.BaseType)),"[DataContract]" + Environment.NewLine); }
4.添加[DataMember]属性
1 2 3 4 5 6 7 8 9 10 11 public string Property (EdmProperty edmProperty ){ return string .Format( CultureInfo.InvariantCulture, "{5}\t{0} {1} {2} { { {3}get;{4}set; }}" , Accessibility.ForProperty(edmProperty), _typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty), _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), _code.SpaceAfter(Accessibility.ForSetter(edmProperty)),"[DataMember]" + Environment.NewLine); }
2. EF引起WCF停止的问题 在调试中发现数据底层处理正常,但将数据封装成实体类并传送给客户端的时候报错,服务异常停止。这是由于代理类Proxy Types产生的影响,在程序运行过程中可以在EF的操作结果后设置断点,会看到封装的类型并不是实体类,举例来说,EF中有实体类Person,但在操作结果封装的时候并不是用Person,而是用了它的一个代理类,如Person123。如果我们给EF建立了实体类关联,如Person的关联子类Job,那么在序列化的过程中,代理类Person123没法找到Job类,因为代理类没有设置关联,所以产生了错误。解决这个问题的方法禁用实体的代理类。
1.进入实体的Context模板
2.修改实体模型初始化方法
1 2 3 4 5 6 7 8 9 10 11 12 13 public <#=code.Escape(container)#>() : base ("name=<#=container.Name#>" ) { <# if (!loader.IsLazyLoadingEnabled(container)) { #> this .Configuration.LazyLoadingEnabled = false ; <# } #> this .Configuration.ProxyCreationEnabled = false ; }
3. EF使用INotifyChangedProperty接口的问题 在EF5.0中使用T4模板从数据库生成实体模型并不会使用INotifyChangedProperty接口,所以如果需要,可以修改模板,方法和问题1一样:
1.添加引用,修改UsingDirectives
:
1 2 3 4 5 6 7 8 9 10 11 12 public string UsingDirectives (bool inHeader, bool includeCollections = true ){ return inHeader == string .IsNullOrEmpty(_code.VsNamespaceSuggestion()) ? string .Format( CultureInfo.InvariantCulture, "{0}using System;{1}{3}{4}" + "{2}" , inHeader ? Environment.NewLine : "" , includeCollections ? (Environment.NewLine + "using System.Collections.Generic;" ) : "" , inHeader ? "" : Environment.NewLine, Environment.NewLine + "using System.Runtime.Serialization;" , Environment.NewLine +"using System.ComponentModel;" ) : "" ; }
2.添加INotifyChanged接口,修改EntityClassOpening
1 2 3 4 5 6 7 8 9 10 public string EntityClassOpening (EntityType entity ){ return string .Format( CultureInfo.InvariantCulture, "{4}{0} {1}partial class {2}{3}:INotifyPropertyChanged" , Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore(" : " , _typeMapper.GetTypeName(entity.BaseType)),"[DataContract]" + Environment.NewLine); }
3.实现INotifyChanged接口,找到codeStringGenerator.EntityClassOpening
,在下面添加
1 2 3 4 5 6 7 8 9 10 11 <#=codeStringGenerator.UsingDirectives(inHeader: false)#> <#=codeStringGenerator.EntityClassOpening(entity)#> { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged (string name ) { if (PropertyChanged!= null ) { PropertyChanged(this , new PropertyChangedEventArgs(name)); } }
4.重写访问控制器,修改Property
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public string Property (EdmProperty edmProperty ){ return "[DataMember]" + Environment.NewLine + "\tprivate " + _typeMapper.GetTypeName(edmProperty.TypeUsage) + " _" + _code.Escape(edmProperty) + ";" + Environment.NewLine + "\tpublic " + _typeMapper.GetTypeName(edmProperty.TypeUsage) + " " + _code.Escape(edmProperty) + Environment.NewLine + "\t{" + Environment.NewLine + "\t\tget" + Environment.NewLine + "\t\t{" + Environment.NewLine + "\t\t\treturn " + "_" + _code.Escape(edmProperty) + ";" + Environment.NewLine + "\t\t}" + Environment.NewLine + "\t\tset" + Environment.NewLine + "\t\t{" + Environment.NewLine + "\t\t\t_" + _code.Escape(edmProperty) + "=value;" + Environment.NewLine + "\t\t\tOnPropertyChanged(\"" +_code.Escape(edmProperty) + "\");" +Environment.NewLine + "\t\t}" +Environment.NewLine + "\t}" ; }
4. EF禁用代理模式后不能在服务端使用导航属性的问题 在问题2中,使用了this.Configuration.ProxyCreationEnabled = false;
禁用EF代理模式,但是造成的后果是不能在服务端使用实体的导航属性,获取的所有导航属性均为null.如果需要在服务端使用导航属性,可以将代理模式开启this.Configuration.ProxyCreationEnabled = true;
同时需要解决问题2,可以在服务端返回客户端的接口上加上自定义的属性ApplyProxyDataContractResolverAttribute:
1 2 3 [OperationContract ] [ApplyProxyDataContractResolverAttribute ] TestModel GetData (string fileid ) ;
ApplyProxyDataContractResolverAttribute的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public class ProxyDataContractResolver : DataContractResolver { private XsdDataContractExporter _exporter = new XsdDataContractExporter(); public override Type ResolveName (string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver ) { return knownTypeResolver.ResolveName( typeName, typeNamespace, declaredType, null ); } public override bool TryResolveType (Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace ) { Type nonProxyType = ObjectContext.GetObjectType(dataContractType); if (nonProxyType != dataContractType) { XmlQualifiedName qualifiedName = _exporter.GetSchemaTypeName(nonProxyType); XmlDictionary dictionary = new XmlDictionary(2 ); typeName = new XmlDictionaryString(dictionary, qualifiedName.Name, 0 ); typeNamespace = new XmlDictionaryString(dictionary, qualifiedName.Namespace, 1 ); return true ; } else { return knownTypeResolver.TryResolveType( dataContractType, declaredType, null , out typeName, out typeNamespace); } } } public class ApplyProxyDataContractResolverAttribute : Attribute , IOperationBehavior { public void AddBindingParameters (OperationDescription description, BindingParameterCollection parameters ) { } public void ApplyClientBehavior (OperationDescription description, ClientOperation proxy ) { DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>(); dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver(); } public void ApplyDispatchBehavior (OperationDescription description, DispatchOperation dispatch ) { DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>(); dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver(); } public void Validate (OperationDescription description ) { } }