3

我有一个数据合同:

[DataContract]
public class Entity
{
   [DataMember]
   public int? Temp
   {
       get;
       set;
   }
   //other properties
}

我需要Entity根据数据库表信息创建对象。我必须使用SqlDataReader(要求),然后将列映射到实体属性。Description我创建了描述使用属性映射的辅助类:

public class Entity
{
   [Description("TempColumnName")]
   public int? Temp
   {
       get;
       set;
   }
   //other properties
}

在这种情况下如何避免创建额外的类?我认为使用数据契约属性名称或使用附加属性装饰数据契约是一个坏主意。你有什么建议吗?

4

3 回答 3

3

您的数据合约代表您的服务向外界公开的公共 API。一旦您的服务开始被客户端使用,您希望避免对本合同进行重大更改 - 特别是如果您的服务正被您无法控制的客户端使用。当然,如果您的服务仅用于“内部”使用,那么您对此具有一定的灵活性。

凭借“程序到抽象 - 而不是实现”理念 - WCF 服务的数据(和服务)契约在面向服务的上下文中提供抽象,类似于面向对象世界中的接口。

如果您使用 .dll 共享您的数据合同,那么在我看来 - 您无法绕过这样一个事实,即您需要单独的类来表示您的服务数据合同和您的数据模型。使用像AutoMapper这样的映射工具可以显着减少转换代码的数量。如果你试图在你的数据合约中塞入额外的信息(比如通过数据成员的属性)——你最终会将关于你的数据访问方法的信息泄露到你发布的二进制文件中。如果将来您切换到另一种数据访问方法——甚至是 NoSQL 数据库,会发生什么?您对数据合同的更改可能会破坏使用旧版本二进制文件的现有客户端。

在我提出替代方案之前 - 让我说以上是我在所有情况下都会做的事情。你的数据模型和你的服务 API 代表着根本不同的野兽,它们会因不同的因素而相互独立地变化。尽管事实上它们通常看起来很相似,但两者的使用环境和可能发生的变化是完全不同的。

现在进行黑客攻击...

如果您通过 MEX 端点发布服务 API,则当 Visual Studio(或真正的 svcutil.exe)生成代理时 - 客户端会获得自己的服务和数据合同版本。在服务方面,您可以执行以下操作:

[DataContract]
public class Entity
{
    [DataMember]
    public int? Temp { get; set; }

    [Description]
    public int? DbTemp { get; set; }

    [OnSerializing()]
    internal void OnSerializingMethod(StreamingContext context)
    {
        Temp = DbTemp;
    }
}

Entity数据库填充实例时,将DbTemp设置属性。只有当实例被序列化以准备发送到客户端时,Temp才会填充该属性。

当您的客户端从 MEX 端点生成代理和数据合约时,Entity数据合约如下所示:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Entity", Namespace="http://schemas.datacontract.org/2004/07/WcfService1")]
[System.SerializableAttribute()]
public partial class Entity : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

    [System.NonSerializedAttribute()]
    private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private System.Nullable<int> TempField;

    [global::System.ComponentModel.BrowsableAttribute(false)]
    public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
        get {
            return this.extensionDataField;
        }
        set {
            this.extensionDataField = value;
        }
    }

    [System.Runtime.Serialization.DataMemberAttribute()]
    public System.Nullable<int> Temp {
        get {
            return this.TempField;
        }
        set {
            if ((this.TempField.Equals(value) != true)) {
                this.TempField = value;
                this.RaisePropertyChanged("Temp");
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName) {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null)) {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

不是您的数据访问实现的嗅探!

最后 - 请注意,您可以通过发布 .dll 来完成上述技巧 - 您只需发布包含上述“精简”数据合同的 .dll。但是,这意味着您必须有效地管理两个不同版本的Entity- 您的服务内部使用的版本和已发布的 .dll 中的版本。如果这些不同步 - 祝你调试成功!

于 2013-11-02T18:30:30.327 回答
0

将 description 属性放在数据协定中,WCF 不会使用它,任何需要描述的进程都可以使用它:

[DataContract]
public class Entity
{
   [DataMember]
   [Description("TempColumnName")]
   public int? Temp
   {
       get;
       set;
   }
   //other properties
}
于 2013-10-28T05:43:15.773 回答
0

如果你想避免中间类,也许你需要的是 automapper 之类的东西。它可用于从 IDataTable 以最少的配置进行映射,尤其是在列名与字段名相同的琐碎情况下。

https://github.com/AutoMapper/AutoMapper

http://www.geekytidbits.com/automapper-with-datatables/

于 2013-10-29T06:10:28.993 回答