将属性从Excel数据表映射到列表时出现问题

帕沙

知名会员
已加入
2020年4月29日
留言内容
64
编程经验
1-3
我有testdata模型,并且我想将列值映射到现有的类层次结构属性,并且当前它不映射值并显示NULL值

例如在测试数据模型中,我有一个字段paymentOptions,而在Excel工作表中,该值是但在要列出的数据表的映射之后-paymentOptions的值显示为NULL

测试数据模型:


Test Data Model:
using ContradoWebsiteAutoFramework.Constants;
using ContradoWebsiteAutoFramework.Model;
using ContradoWebsiteAutoFramework.Model.PaymentOptions;
using Framework.Model.Excel;
using System.Collections.Generic;

namespace ContradoWebSiteAutoFramework.Model.Excel
{
    public partial class TestDataModel
    {
    
        public TestDataModel() {

        
        }
    
    
        [DataNames("TestName")]
        public string TestName { get; set; }

    

        [DataNames("productId")]
        public int productId { get; set; }

        [DataNames("orderId")]
        public int orderId { get; set; }

    
        [DataNames("designMethod")]
        public DesignMethod designMethod { get; set; }

        [DataNames("signedIn")]
        public bool signedIn { get; set; }

        [DataNames("increaseBasketQty")]
        public bool increaseBasketQty { get; set; }

        [DataNames("signedInCMS")]
        public bool signedInCMS { get; set; }

        [DataNames("editable")]
        public bool editable { get; set; }

        [DataNames("paymentOptions")]
        public PaymentOptions paymentOptions { get; set; }

        [DataNames("checkInVoice")]
        public bool checkInVoice { get; set; }

        [DataNames("navigateToDesign")]
        public bool navigateToDesign { get; set; }

        [DataNames("checkOrderAuthorsie")]
        public bool checkOrderAuthorsie { get; set; }

        [DataNames("checkSplitOrder")]
        public bool checkSplitOrder { get; set; }

        [DataNames("SiteId")]
        public string SiteId { get; set; }

        [DataNames("SiteUrl")]
        public string SiteUrl { get; set; }

        [DataNames("CultureCode")]
        public string CultureCode { get; set; }

        [DataNames("SiteGroupId")]
        public string SiteGroupId { get; set; }

        [DataNames("NickName")]
        public string NickName { get; set; }

        [DataNames("byCard")]
        public 克拉纳期权 byCard { get; set; }

        [DataNames("payLater")]
        public bool payLater { get; set; }

        [DataNames("sliceIt")]
        public bool sliceIt { get; set; }

        [DataNames("portal")]
        public PaymentPortal portal { get; set; }

        [DataNames("delivery")]
        public DeliveryMethod delivery{get;set;}

    }

}

用于将数据表转换为列表的映射器:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Model.Excel
{
    public class DataNamesMapper<TEntity> where TEntity : class, new()
    {
        public TEntity Map(DataRow row)
        {
            TEntity entity = new TEntity();
            return Map(row, entity);
        }

        public TEntity Map(DataRow row, TEntity entity)
        {
            var columnNames = row.Table.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToList();
            var properties = (typeof(TEntity)).GetProperties()
                                              .Where(x => x.GetCustomAttributes(typeof(DataNamesAttribute), true).Any())
                                              .ToList();
            foreach (var prop in properties)
            {
                PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
            }

            return entity;
        }

        public IEnumerable<TEntity> Map(DataTable table)
        {
            清单<TEntity> entities = new 清单<TEntity>();
            var columnNames = table.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToList();
            var properties = (typeof(TEntity)).GetProperties()
                                              .Where(x => x.GetCustomAttributes(typeof(DataNamesAttribute), true).Any())
                                              .ToList();
            foreach (DataRow row in table.Rows)
            {
                TEntity entity = new TEntity();
                foreach (var prop in properties)
                {
                    PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
                }
                entities.Add(entity);
            }

            return entities;
        }
    }
}

Property Map 救命er:
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Model.Excel
{
    public static class PropertyMapHelper
    {

        public static void Map(Type type, DataRow row, PropertyInfo prop, object entity)
        {
            清单<string> columnNames = AttributeHelper.GetDataNames(type, prop.Name);

            foreach (var columnName in columnNames)
            {
                if (!String.IsNullOrWhiteSpace(columnName) && row.Table.Columns.Contains(columnName))
                {
                    var propertyValue = row[columnName];
                    if (propertyValue != DBNull.Value)
                    {
                        ParsePrimitive(prop, entity, row[columnName]);
                        break;
                    }
                }
            }
        }

        private static void ParsePrimitive(PropertyInfo prop, object entity, object value)
        {
            if (prop.PropertyType == typeof(string))
            {
                prop.SetValue(entity, value.ToString().Trim(), null);
            }
            else if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
            {
                if (value == null)
                {
                    prop.SetValue(entity, null, null);
                }
                else
                {
                    prop.SetValue(entity, ParseBoolean(value.ToString()), null);
                }
            }
            else if (prop.PropertyType == typeof(long))
            {
                prop.SetValue(entity, long.Parse(value.ToString()), null);
            }
            else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
            {
                if (value == null)
                {
                    prop.SetValue(entity, null, null);
                }
                else
                {
                    prop.SetValue(entity, int.Parse(value.ToString()), null);
                }
            }
            else if (prop.PropertyType == typeof(decimal))
            {
                prop.SetValue(entity, decimal.Parse(value.ToString()), null);
            }
            else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
            {
                double number;
                bool isValid = double.TryParse(value.ToString(), out number);
                if (isValid)
                {
                    prop.SetValue(entity, double.Parse(value.ToString()), null);
                }
            }
            else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(Nullable<DateTime>))
            {
                DateTime date;
                bool isValid = DateTime.TryParse(value.ToString(), out date);
                if (isValid)
                {
                    prop.SetValue(entity, date, null);
                }
                else
                {
                    isValid = DateTime.TryParseExact(value.ToString(), "MMddyyyy", new CultureInfo("en-US"), DateTimeStyles.AssumeLocal, out date);
                    if (isValid)
                    {
                        prop.SetValue(entity, date, null);
                    }
                }
            }
 else if (prop.PropertyType.IsEnum)
            {
                var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                var enumValue = 枚举.Parse(type,value.ToString(), true);
                prop.SetValue(entity, enumValue, null);
            }
            else if (prop.PropertyType == typeof(Guid))
            {
                Guid guid;
                bool isValid = Guid.TryParse(value.ToString(), out guid);
                if (isValid)
                {
                    prop.SetValue(entity, guid, null);
                }
                else
                {
                    isValid = Guid.TryParseExact(value.ToString(), "B", out guid);
                    if (isValid)
                    {
                        prop.SetValue(entity, guid, null);
                    }
                }


            }
         
        }

        public static bool ParseBoolean(object value)
        {
            if (value == null || value == DBNull.Value) return false;

            switch (value.ToString().ToLowerInvariant())
            {
                case "1":
                case "y":
                case "yes":
                case "true":
                    return true;

                case "0":
                case "n":
                case "no":
                case "false":
                default:
                    return false;
            }
        }
    }
}

我想将列映射到的自定义属性类型:
using ContradoWebsiteAutoFramework.Entities;

namespace ContradoWebsiteAutoFramework.Model.PaymentOptions
{
    public class PaymentOptions
    {
        public PaymentPortal portal;
        public DeliveryMethod delivery = DeliveryMethod.Billing;

        public PaymentOptions()
        {
        }
        public PaymentOptions(Site site)
        {

        }
    }
}
DataName属性类别:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Model.Excel
{
    [AttributeUsage(AttributeTargets.Property)]
    public class DataNamesAttribute : Attribute
    {
        protected 清单<string> _valueNames { get; set; }

        public 清单<string> ValueNames
        {
            get
            {
                return _valueNames;
            }
            set
            {
                _valueNames = value;
            }
        }

        public DataNamesAttribute()
        {
            _valueNames = new 清单<string>();
        }

        public DataNamesAttribute(params string[] valueNames)
        {
            _valueNames = valueNames.ToList();
        }
    }
}
自定义类被进一步继承:
namespace ContradoWebsiteAutoFramework.Model.PaymentOptions
{
    public class 克拉纳期权 : PaymentOptions
    {
        //default - don't use card payment by deffault
        public bool byCard = false;
        public bool payLater = false;
        public bool sliceIt = false;
        public 克拉纳期权()
        {
            portal = PaymentPortal.Klarna;
        }
    }
}
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
Where is the code for PropertyMapHelper?

您确定该循环中的代码已执行吗?
C#:
           foreach (var prop in properties)
            {
                PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
            }
 

帕沙

知名会员
已加入
2020年4月29日
留言内容
64
编程经验
1-3
Where is the code for PropertyMapHelper?

您确定该循环中的代码已执行吗?
C#:
           foreach (var prop in properties)
            {
                PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
            }
是的,但是对于自定义属性类型,即对于paymentOptions /门户网站列,它不属于“映射方法”中提到的任何条件
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
Nice that paymentOptions and portal are enums.

Look closely at the code for PropertyMapHelper.ParsePrimitive(). Notice that there is no code there for handling parsing enums. Looks like you'll need to add code for doing that parsing.
 

帕沙

知名会员
已加入
2020年4月29日
留言内容
64
编程经验
1-3
Nice that paymentOptions and portal are enums.

Look closely at the code for PropertyMapHelper.ParsePrimitive(). Notice that there is no code there for handling parsing enums. Looks like you'll need to add code for doing that parsing.
嘿,是的,我没有处理"Enum"映射,添加代码后可用于几列,

C#:
 else if (prop.PropertyType.IsEnum)
            {
                var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                var enumValue = 枚举.Parse(type,value.ToString(), true);
                prop.SetValue(entity, enumValue, null);
            }

但是对于付款方式,我仍然没有得到设置的值,因为

目前在我的Excel中它具有作为 "KlarnaOptions"并在映射后显示为空,并且在我的代码中,Klarnaptions类再次从PaymentOptions继承,因此我该如何映射

在excel映射之前,我们曾经按如下所示设置值,因此,对于那些在这种情况下进一步继承的所有选项,我没有获得在excel中传递的值,我不知道如何映射这种情况


C#:
paymentOptions = new 克拉纳期权()
                {
                    delivery = DeliveryMethod.Billing,
                    byCard = true
                }
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
在线程中添加了属性映射帮助器功能
请停止编辑您的原始问题。这不是StackOverflow的习惯用法,并且期望继续研究该问题。这是一个标准论坛,其中有一个与帖子线索相关的时间表。如果人们不仔细查看编辑时间,这会让人们感到奇怪,为什么我似乎要在帖子#1中出现时询问帖子#5中的mapper helper类……但这仅仅是因为您在之后添加了它看到帖子#5。同样的处理,在第7个帖子中,我说了一些关于未解析枚举的事,然后突然出现在第1个帖子中。这使我看起来好像在吸毒或不专心。
 

帕沙

知名会员
已加入
2020年4月29日
留言内容
64
编程经验
1-3
请停止编辑您的原始问题。这不是StackOverflow的习惯用法,并且期望继续研究该问题。这是一个标准论坛,其中有一个与帖子线索相关的时间表。如果人们不仔细查看编辑时间,这会让人们感到奇怪,为什么我似乎要在帖子#1中出现时询问帖子#5中的mapper helper类……但这仅仅是因为您在之后添加了它看到帖子#5。同样的处理,在第7个帖子中,我说了一些关于未解析枚举的事,然后突然出现在第1个帖子中。这使我看起来好像在吸毒或不专心。
哦,对此感到抱歉 :( -我不知道标准。现在不再编辑原始线程!!

@skydriver您可以帮助解决此映射问题吗?
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
似乎很明显:就像您没有用于处理枚举的代码一样,看起来您也没有用于处理自定义类的代码。不是,您也必须添加该代码。

出于好奇,为什么在已经存在诸如AutoMapper之类的功能强大的库的情况下,为什么还要滚动自己的映射器?
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
我觉得奇怪的是,如果您自己编写此代码,可能会丢失代码。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
我懂了。没有什么比编写自己的代码更好,更可靠了。至少如果有什么问题,您会从哪里开始寻找...
 

帕沙

知名会员
已加入
2020年4月29日
留言内容
64
编程经验
1-3
@跳伞 -我是这个编码领域的新手,所以对可以采取的可能的方法知之甚少! -对于自定义类,我仍在寻找可能的解决方案,是否应该使用Automapper软件包来获得所需的结果?

需要您的一点帮助来了解我应该采用哪种方法,以及如何从该方法开始?
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
大多数自动映射器ORM都使用Entity Framework 6。如果您想知道为什么我不喜欢EF,请在论坛上搜索我的名字以及关键字entity framework,您会发现我的所有原因。

如果您的目标是学习语言和框架,那么请不要使用自动映射器。并研究有关您要执行的操作的文档。

如果您的目标是完成项目,而您希望以最小的努力完成该项目,那么一些人可能会推荐一个自动映射器。如果您选择那条路线,我会请小巧的人。

ORM并非一帆风顺,它们并非总是开箱即用,有时您需要涉猎生成的代码。

我不明白,您喜欢我说过编写自己的代码的建议,但是在下一口气中,您会询问有关自动发电机的信息,以帮助解决您的问题...
 

帕沙

知名会员
已加入
2020年4月29日
留言内容
64
编程经验
1-3
大多数自动映射器ORM都使用Entity Framework 6。如果您想知道为什么我不喜欢EF,请在论坛上搜索我的名字以及关键字entity framework,您会发现我的所有原因。

如果您的目标是学习语言和框架,那么请不要使用自动映射器。并研究有关您要执行的操作的文档。

如果您的目标是完成项目,而您希望以最小的努力完成该项目,那么一些人可能会推荐一个自动映射器。如果您选择那条路线,我会请小巧的人。

ORM并非一帆风顺,它们并非总是开箱即用,有时您需要涉猎生成的代码。

我不明白,您喜欢我说过编写自己的代码的建议,但是在下一口气中,您会询问有关自动发电机的信息,以帮助解决您的问题...
我完全理解您的观点,现在我唯一担心的是,此时我剩下的是excel列值的自定义类映射-我需要像枚举一样编写自己的代码,但是我不知道在哪里我应该从哪里开始,或者我应该搜索什么,所以我得到了一些例子,可以为我的课程实现 :(
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
我专门指的是Automapper:

它没有专门与EntityFramework绑定。无论何时您需要将一种对象映射到另一种对象。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
我不知道从哪里开始或应该搜索什么,所以我得到了一些例子并可以为我的课程实施
这取决于您是否要像博客作者一样编写通用的解决方案,还是要针对特定​​问题编写特定的解决方案。针对您的特定问题编写代码将涉及较少的总体代码,并且更易于理解,但是它将与您的类紧密结合。

Basically, you would have a ParseObjects() method whose job is to instantiate and fill in object properties. That means your ParsePrimitive() should return whether it parsed a primitive value or not, and that you should have something that checks that so that it will call your ParseObjects() instead.

Within te ParseObjects() you would again inspect the type of the property to determine if it's one of your known class types. You then would instantiate the appropriate class, and assign that instance to the property of the parent object. The next you would start reading values out of your row and setting them on properties of that instance (as opposed to the ParsePrimitive() which was setting the the values directly to the property of the parent object). If you are smart, you can just call ParsePrimitive() and pass in this new instance instance instead of the parent object. You may need to do this recursively if this new instance also has non-primitive properties.

For the generic solution, you would need to change the code for the DataNamesAttribute to also take an optional class type. This is for the case when the property type is base or abstract type, but you want instantiate a specific type further down the hierarchy. Then in the ParseObjects() class instead of checking for your well known class types, you would have to instantiate either the type specified in the attribute, or if not specified in the attribute, the type of the property. Then the rest is the same as the previous paragraph regarding setting parent object properties and child object properties.
 
最佳 底部