如何避免在将数据库表映射到数据协定时创建其他类

本文关键字:数据 定时 创建 其他 映射 何避免 数据库 | 更新日期: 2023-09-27 18:36:47

我有一个数据协定:

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

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

如何避免在将数据库表映射到数据协定时创建其他类

数据协定表示服务向外界公开的公共 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中使用的版本。 如果这些不同步 - 祝你调试那个好运!

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

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

避免中间类,也许你需要的是像自动映射器这样的东西。它可用于以最少的配置从 IDataTable 映射,尤其是在列名与字段名相同的 trival 情况下。

https://github.com/AutoMapper/AutoMapper

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