存储过程 returns 复杂类型,我使用 AutoMapper 将其映射到我的模型,但未设置外键对象

Stored procedure returns complex type, I use AutoMapper to map it to my model but foreign key objects do not get set

我使用 AutoMapper 从我的 DataLayer(EF6 DbFirst AutoGenerated 层)映射到我的 ModelLayer(Pocos/Dtos)。我感到困惑的一件事是,当使用存储过程时,它们 return 是一个复杂的对象,例如 Customer_GetCustomers_Result 所以我必须使用 AutoMapper 将其映射到我的 CustomerPoco 并且我丢失了外键对象属性,例如 Customer.Address 因为它们不属于我的复杂类型。

这是我的 CustomerPoco 的示例:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }
    public string Fax { get; set; }
    public string Website { get; set; }
    public int AddressId { get; set; }

    // THESE END UP NOT GETTING SET
    public List<CustomerContact> Contacts { get; set; }
    public Address Address { get; set; }
}

在我的 GetCustomers 存储过程中,假设我做了一些简单的事情,例如:SELECT * FROM CUSTOMER。它 return 是一个复杂的对象 context.Customer_GetCustomers_Result。然后我必须将这个复杂的对象映射到我的 CustomerPoco。联系人和地址未设置。我该怎么办?

这是我给客户的映射:

    CreateMap<Customer, Model.Customer>()
        .ForMember(dest => dest.Contacts, opt => opt.MapFrom(src => src.CustomerContacts));

    CreateMap<Customer_GetCustomers_Result, Model.Customer>();

这实际上可能是个糟糕的问题。我相信,如果我真的想对 return 我的列表使用存储过程,我将需要进行连接,实际上 return 我的 select 中地址的属性,例如... a.Address1 、a.Address2 等。那么有没有办法将它们映射到 Model。Customer.Address 从这里即使它们都被拆分成单独的属性?

我认为您在这里有两个不同的问题需要解决。首先是如何将存储过程获取到 return 并将所有数据绑定到您的 EF 复杂类型中。有几篇文章 here (blog) and here (CodeProject) 应该有所帮助。本质上,将存储过程写入 return 多个结果集,以及几行代码以前进到下一个结果集并将其读入上下文。

要从 AutoMapper 获得您想要的映射,您还需要为用作 属性 的每种类型添加映射(例如,从您的 EF 'Address' 类型到您的POCO 'Address' 类型).

这实际上相当简单,但您可能需要设置一些 AutoMapper 配置。您的存储过程实际上只能 return 扁平化结果,但您仍然可以传递所有关系的所有字段。如果你想几乎没有配置,你真的只需要让你的存储过程 return 和 属性 名称以扁平格式。例如,AddressCity 而不是 City。如果您映射的对象 from 有一个 属性 像 AddressCity,并且您映射的对象 to有一个导航 属性 Address,它本身有一个 属性 City,AutoMapper 会自动适当地填充它。如果您不想或不能更改由存储过程编辑的列名 return,那么您只需配置 AutoMapper:

AutoMapper.Mapper.CreateMap<Customer_GetCustomers_Result, Custom>()
    .ForMember(dest => dest.Address, opts => opts.MapFrom(src => new Address
    {
        City = src.City,
        // etc.
    });

如果您有更复杂的场景,例如可枚举导航 属性,此方法将无法正常工作。在这种情况下,最好的办法是分多个步骤进行,这样您就可以使用一个存储过程来检索主要对象以及可以用它扁平化的所有内容。这样,您就可以映射到所需的类型。然后,使用单独的存储过程根据主要对象的 id 或其他内容查找可枚举项,并将它们映射到先前映射对象上的可枚举 属性。

public IEnumerable<object> sample()
   {


        using (SqlConnection con = new SqlConnection(connStr))
        {
            DataTable dt = new DataTable();
            SqlCommand cmd = new SqlCommand("proedurename", con);
            cmd.CommandType = CommandType.StoredProcedure;
            if (cmd.Connection.State != ConnectionState.Open)
                cmd.Connection.Open();

            var retObject = new List<dynamic>();
            using (var dataReader = cmd.ExecuteReader())
            {
                while (dataReader.Read())
                {
                    var dataRow = new ExpandoObject() as IDictionary<string, object>;
                    for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++)
                    {
                        dataRow.Add(
                            dataReader.GetName(iFiled),
                            dataReader.IsDBNull(iFiled) ? null : dataReader[iFiled] // use null instead of {}
                        );
                    }

                    retObject.Add((ExpandoObject)dataRow);
                }
            }
            return retObject;
        }

    }