【问题标题】:Generic object to object mapping with parametrized constructor使用参数化构造函数的通用对象到对象映射
【发布时间】:2011-02-24 15:48:48
【问题描述】:

我有一个返回 IDataRecord 的数据访问层。 我有一个为 DataContracts (dto's) 提供服务的 WCF 服务。这些 DataContract 由包含 IDataRecord 的参数化构造函数启动,如下所示:

[DataContract]
public class DataContractItem
{
    [DataMember]
    public int ID;
    [DataMember]
    public string Title;

    public DataContractItem(IDataRecord record)
    {
        this.ID = Convert.ToInt32(record["ID"]);
        this.Title = record["title"].ToString();
    }
}

很遗憾,我无法更改 DAL,因此我不得不使用 IDataRecord 作为输入。但总的来说,这非常有效。映射大部分时间都非常简单,有时它们会更复杂一些,但不是火箭科学。

但是,现在我希望能够使用泛型来实例化不同的 DataContract 以简化 WCF 服务方法。我希望能够做类似的事情:

public T DoSomething<T>(IDataRecord record) {
    ...
return new T(record);
}

所以我尝试了以下解决方案:

  1. 使用带有构造函数的泛型类型接口。 不起作用:当然我们不能在接口中定义构造函数

  2. 使用静态方法实例化 DataContract 并创建包含此静态方法的类型化接口。 不起作用:当然我们不能在接口中定义静态方法

  3. 使用包含 new() 约束的泛型类型接口 不起作用:new() 约束不能包含参数(IDataRecord)

  4. 使用工厂对象执行基于 DataContract 类型的映射。 确实有效,但是:不是很干净,因为我现在有一个 switch 语句,所有映射都在一个文件中。

我找不到真正干净的解决方案。有人可以为我解释一下吗? 该项目对于任何复杂的映射技术来说都太小,对于“基于开关”的工厂实现来说太大了。

【问题讨论】:

    标签: c# wcf generics object-object-mapping


    【解决方案1】:

    我认为您可以为此使用 AutoMapper。这是一个开源项目,它可以让你做你想做的事。在你做某事时,你会这样做:

    public TDestination DoSomething<TDestination>(IDataRecord record) {
       return Mapper.Map(reader, reader.GetType(), typeof(TDestination));
    }
    

    在此之前,您需要设置地图

    Mapper.CreateMap<IDataRecord, MyDestinationType>()
    

    【讨论】:

    • 我其实是这样走的。我知道 automapper,但我认为它对于这种情况来说太复杂了。然而事实证明这并不复杂而且非常容易!谢谢!
    【解决方案2】:

    您可以通过反射调用类型构造函数:

    public T DoSomething<T>(IDataRecord record)
    {
        //do something...
    
        var ci = typeof(T).GetConstructor(new[] { typeof(IDataRecord) });
        return (T)ci.Invoke(new object[] { record });
    }
    

    编辑:上述方法非常脆弱并且依赖于约定,因此另一种方法是为您的数据合约创建一个允许初始化的接口:

    public interface IDataContract
    {
        void Initialise(IDataRecord record);
    }
    
    public T DoSomething<T>(IDataRecord record) where T : IDataContract, new()
    {
        //do something
    
        T contract = new T();
        contract.Initialise(record);
    
        return contract;
    }
    

    【讨论】:

    • true,但我希望能够确保所有 DataContracts 都实现该构造函数,因此我想为 DataContracts 使用一个接口。但是,如果我使用反射,我不确定“构造”的 DataContract 是否具有正确的构造函数。
    【解决方案3】:

    这就是我的做法..

    DataRow 的扩展方法,可让我将业务对象转换为 DataRow,反之亦然。

    /// <summary>
    /// Extension methods for DataRow.
    /// </summary>
    public static class DataRowExtensions
    {
        /// <summary>
        /// Converts DataRow into business object.
        /// </summary>
        /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam>
        /// <param name="dataRow">DataRow object to convert to business object.</param>
        /// <returns>business object created from DataRow.</returns>
        public static TEntity ToEntity<TEntity>(this DataRow dataRow)
            where TEntity : EntityBase, new()
        {
            TEntity entity = new TEntity();
            ExtensionHelper.TransformDataRowToEntity<TEntity, DataRow>(ref dataRow, ref entity);
    
            return entity;
        }
    
        /// <summary>
        /// Converts business object into DataRow.
        /// </summary>
        /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam>
        /// <param name="dataRow">DataRow object to convert business object into.</param>
        /// <param name="entity">Business object which needs to be converted to DataRow.</param>
        public static void FromEntity<TEntity>(this DataRow dataRow, TEntity entity)
            where TEntity : EntityBase, new()
        {
            ExtensionHelper.TransformEntityToDataRow<TEntity, DataRow>(ref entity, ref dataRow);
        }
    }
    

    执行转换的实际辅助方法..

    /// <summary>
    /// Helper methods for transforming data objects into business objects and vice versa.
    /// </summary>
    /// <remarks>
    /// <para>Most important implementation that takes care of universal transformation between business objects and data object.</para>
    /// <para>Saves programmers from writing the same old code for every object in the system.</para>
    /// </remarks>
    public static class ExtensionHelper
    {
        /// <summary>
        /// Transforms business object into DataRow.
        /// </summary>
        /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam>
        /// <typeparam name="TDataRow">A type that inherits DataRow.</typeparam>
        /// <param name="entity">business object which is transformed into DataRow object.</param>
        /// <param name="dataRow">DataRow object which is transformed from business object.</param>
        public static void TransformEntityToDataRow<TEntity, TDataRow>(ref TEntity entity, ref TDataRow dataRow)
            where TDataRow : DataRow
            where TEntity : EntityBase
        {
            IQueryable<DataField> entityFields = entity.GetDataFields();
    
            foreach (DataColumn dataColoumn in dataRow.Table.Columns)
            {
                if (!dataColoumn.ReadOnly)
                {
                    var entityField =
                        entityFields.Single(e => e.DataFieldMapping.MappedField.Equals(dataColoumn.ColumnName, StringComparison.OrdinalIgnoreCase));
    
                    if (entityField.Property.GetValue(entity, null) == null)
                    {
                        if (dataColoumn.AllowDBNull)
                        {
                            dataRow[dataColoumn] = System.DBNull.Value;
                        }
                        else
                        {
                            throw new Exception(dataColoumn.ColumnName + " cannot have null value.");
                        }
                    }
                    else
                    {
                        if (entityField.Property.GetType().IsEnum)
                        {
                            dataRow[dataColoumn] = Convert.ToByte(entityField.Property.GetValue(entity, null));
                        }
                        else
                        {
                            dataRow[dataColoumn] = entityField.Property.GetValue(entity, null);
                        }
                    }
                }
            }
        }
    
        /// <summary>
        /// Transforms DataRow into business object.
        /// </summary>
        /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam>
        /// <typeparam name="TDataRow">A type that inherits DataRow.</typeparam>
        /// <param name="dataRow">DataRow object which is transformed from business object.</param>
        /// <param name="entity">business object which is transformed into DataRow object.</param>
        public static void TransformDataRowToEntity<TEntity, TDataRow>(ref TDataRow dataRow, ref TEntity entity)
            where TDataRow : DataRow
            where TEntity : EntityBase
        {
            IQueryable<DataField> entityFields = entity.GetDataFields();
    
            foreach (var entityField in entityFields)
            {
                if (dataRow[entityField.DataFieldMapping.MappedField] is System.DBNull)
                {
                    entityField.Property.SetValue(entity, null, null);
                }
                else
                {
                    if (entityField.Property.GetType().IsEnum)
                    {
                        Type enumType = entityField.Property.GetType();
                        EnumConverter enumConverter = new EnumConverter(enumType);
                        object enumValue = enumConverter.ConvertFrom(dataRow[entityField.DataFieldMapping.MappedField]);
                        entityField.Property.SetValue(entity, enumValue, null);
                    }
                    else
                    {
                        entityField.Property.SetValue(entity, dataRow[entityField.DataFieldMapping.MappedField], null);
                    }
                }
            }
        }
    }
    

    这是我的示例业务对象的外观。请注意自定义属性..

    /// <summary>
    /// Represents User.
    /// </summary>
    [TableMapping("Users", "User", "Users")]
    public class User : EntityBase
    {
        #region Constructor(s)
        /// <summary>
        /// Initializes a new instance of the User class.
        /// </summary>
        public User()
        {
        }
        #endregion
    
        #region Properties
    
        #region Default Properties - Direct Field Mapping using DataFieldMappingAttribute
    
        /// <summary>
        /// Gets or sets the ID value of the AppUser object.
        /// </summary>
        [DataFieldMapping("UserID")]
        [DataObjectFieldAttribute(true, true, false)]
        [NotNullOrEmpty(Message = "UserID From UserDetails Table Is Required.")]
        public override int Id
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets the Username value of the User object.
        /// </summary>
        [DataFieldMapping("UserName")]
        [Searchable]
        [NotNullOrEmpty(Message = "Username Is Required.")]
        public string UserName
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets the FirstName value of the AppUser object.
        /// </summary>
        [DataFieldMapping("FirstName")]
        [Searchable]
        public string FirstName
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets the LastName value of the AppUser object.
        /// </summary>
        [DataFieldMapping("LastName")]
        [Searchable]
        public string LastName
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets the WebSite value of the AppUser object.
        /// </summary>
        [DataFieldMapping("WebSite")]
        [ValidURL(Message = "Website is not in Proper Format.")]
        public string WebSite
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets the ContactNumber value of the AppUser object.
        /// </summary>
        [DataFieldMapping("ContactNumber")]
        [Searchable]
        public string ContactNumber
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets a value indicating whether AppUser Object is active or inactive.
        /// </summary>
        [DataFieldMapping("IsActive")]
        public bool IsActive
        {
            get;
            set;
        }
    
        /// <summary>
        /// Gets or sets the BirthDate value of the AppUser object.
        /// </summary>
        [DataFieldMapping("BirthDate")]
        public DateTime? BirthDate
        {
            get;
            set;
        }
    
        #region Derived Properties
    
        /// <summary>
        /// Gets the full name of the AppUser
        /// </summary>
        public string FullName
        {
            get { return this.FirstName + " " + this.LastName; }
        }
    
        /// <summary>
        /// Gets the Age value of the AppUser
        /// </summary>
        public int Age
        {
            get { return this.BirthDate.HasValue ? this.BirthDate.Value.AgeInYears() : 0; }
        }
    
        #endregion
    
        #endregion
    
    }
    

    这是我的客户端代码..

        /// <summary>
        /// Gets User object by user name.
        /// </summary>
        /// <param name="username">UserName of the user</param>
        /// <returns>User Object</returns>
        public static User GetUserByUsername(string username)
        {
            try
            {
                return Adapter.GetUserByUserName(username)[0].ToEntity<User>();
            }
            catch
            {
                return null;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多