如何在将数据库表映射到数据合同时避免创建其他类?

时间:2013-10-25 07:35:24

标签: c# .net wcf sqldatareader

我有数据合同:

[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
}

在这种情况下,如何避免创建其他类?我认为使用数据合同属性名称或使用其他属性装饰数据合同是一个坏主意。你有什么建议吗?

3 个答案:

答案 0 :(得分: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中使用。如果这些不同步 - 祝你好运!

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

如果你想避免使用中间类,那么你所需要的就像是automapper。它可用于使用最小配置从IDataTable映射,尤其是在列名与字段名相同的trival情况下。

https://github.com/AutoMapper/AutoMapper

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