AutoMapper: Mapping between a IDataReader and DTO object

后端 未结 2 385
情书的邮戳
情书的邮戳 2020-12-17 04:26

I have a DataReader which contains the result of a stored procedure. The naming convention for the columns use underscores for spaces.

I have been able to successfu

相关标签:
2条回答
  • 2020-12-17 05:07

    I downloaded the AutoMapper source and was able do some debugging. I had to alter the CreateBuilder method in DataReaderMapper.cs to get the test to pass.

        private static Build CreateBuilder(Type destinationType, IDataRecord dataRecord)
        {
            var method = new DynamicMethod("DynamicCreate", destinationType, new[] { typeof(IDataRecord) }, destinationType, true);
            var generator = method.GetILGenerator();
    
            var result = generator.DeclareLocal(destinationType);
            generator.Emit(OpCodes.Newobj, destinationType.GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);
    
            for (var i = 0; i < dataRecord.FieldCount; i++)
            {
                var propertyInfo = destinationType.GetProperty(ConvertLowerUnderscoreNamingToPascalNaming(dataRecord.GetName(i)));
                var endIfLabel = generator.DefineLabel();
    
                if (propertyInfo != null && propertyInfo.GetSetMethod(true) != null)
                {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                    generator.Emit(OpCodes.Brtrue, endIfLabel);
    
                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getValueMethod);
                    generator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod(true));
    
                    generator.MarkLabel(endIfLabel);
                }
            }
    
            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);
    
            return (Build)method.CreateDelegate(typeof(Build));
        }
    
        //TODO: refactor to use INamingConvetion and resolve with RegEx pattern
        private static string ConvertLowerUnderscoreNamingToPascalNaming(string original)
        {
            var LowerOriginal = original.ToLower();
            string[] tokens = LowerOriginal.Split('_');
    
            string converted = "";
    
            foreach (var token in tokens)
                converted += token.Substring(0, 1).ToUpper() + token.Substring(1);
    
            return converted;
        }
    
    0 讨论(0)
  • 2020-12-17 05:23

    We have written custom attributes to achieve this. We do mapping-assignment with the help of reflection and here's some sample code for you.

    The attribute that is applied to Business object proerties for coloumn-mapping.

    /// <summary>
        /// Holds mapping information between business objects properties and database table fields.
        /// </summary>
        [global::System.AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
        public sealed class DataFieldMappingAttribute : Attribute
        {
            /// <summary>
            /// Initializes a new instance of the DataFieldMappingAttribute class.
            /// </summary>
            /// <param name="fieldName">Name of the Field in Database Table that the business object properties maps to.</param>
            public DataFieldMappingAttribute(string fieldName)
            {
                this.MappedField = fieldName;
            }
    
            /// <summary>
            /// Gets or Sets the mapped Database Table Field.
            /// </summary>
            public string MappedField
            {
                get;
                private set;
            }
        }
    

    A sample business object would look like this in my application.

    User.cs

    [TableMapping("Users")]
    public class User : EntityBase
    {
        #region Constructor(s)
        public AppUser()
        {
            BookCollection = new BookCollection();
        }
        #endregion
    
        #region Properties
    
        #region Default Properties - Direct Field Mapping using DataFieldMappingAttribute
    
        private System.Int32 _UserId;
    
        private System.String _FirstName;
        private System.String _LastName;
        private System.String _UserName;
        private System.Boolean _IsActive;
    
        [DataFieldMapping("UserID")]
        [DataObjectFieldAttribute(true, true, false)]
        [NotNullOrEmpty(Message = "UserID From Users Table Is Required.")]
        public override int Id
        {
            get
            {
                return _UserId;
            }
            set
            {
                _UserId = value;
            }
        }
    
        [DataFieldMapping("UserName")]
        [Searchable]
        [NotNullOrEmpty(Message = "Username Is Required.")]
        public string UserName
        {
            get
            {
                return _UserName;
            }
            set
            {
                _UserName = value;
            }
        }
    
        [DataFieldMapping("FirstName")]
        [Searchable]
        public string FirstName
        {
            get
            {
                return _FirstName;
            }
            set
            {
                _FirstName = value;
            }
        }
    
        [DataFieldMapping("LastName")]
        [Searchable]
        public string LastName
        {
            get
            {
                return _LastName;
            }
            set
            {
                _LastName = value;
            }
        }
    
        [DataFieldMapping("IsActive")]
        public bool IsActive
        {
            get
            {
                return _IsActive;
            }
            set
            {
                _IsActive = value;
            }
        }
    
        #region One-To-Many Mappings
        public BookCollection Books { get; set; }
    
        #endregion
    
        #region Derived Properties
        public string FullName { get { return this.FirstName + " " + this.LastName; } }
    
        #endregion
    
        #endregion
    
        public override bool Validate()
        {
            bool baseValid = base.Validate();
            bool localValid = Books.Validate();
            return baseValid && localValid;
        }
    }
    

    BookCollection.cs

    /// <summary>
    /// The BookCollection class is designed to work with lists of instances of Book.
    /// </summary>
    public class BookCollection : EntityCollectionBase<Book>
    {
        /// <summary>
        /// Initializes a new instance of the BookCollection class.
        /// </summary>
        public BookCollection()
        {
        }
    
        /// <summary>
        /// Initializes a new instance of the BookCollection class.
        /// </summary>
        public BookCollection (IList<Book> initialList)
            : base(initialList)
        {
        }
    }
    

    Here's the DataRow to BusinessObject transacformation method which is wrapped around the call of an Extension method.

        /// <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);
                    }
                }
            }
        }
    
    0 讨论(0)
提交回复
热议问题