简体   繁体   中英

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

I use AutoMapper to map from my DataLayer(EF6 DbFirst AutoGenerated layer) to my ModelLayer(Pocos/Dtos). One thing I am confused about is when using stored procedures they return a complex object such as Customer_GetCustomers_Result so I have to map this to my CustomerPoco with AutoMapper and I lose foreign key object properties such as Customer.Address since they are not in my complex type.

Here is an example of my 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; }
}

In my GetCustomers stored procedure lets say I do something simple like: SELECT * FROM CUSTOMER. It returns a complex object context.Customer_GetCustomers_Result. Then I have to map this complex object to my CustomerPoco. The Contacts and Address do not get set. How should I go about this?

Here are my mappings for Customer:

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

    CreateMap<Customer_GetCustomers_Result, Model.Customer>();

This actually might have been a bad question. I believe if I really want to use stored procedures to return my lists I will need to do joins and actually return the properties of Address in my select such as... a.Address1, a.Address2, etc. Then is there a way to map them to Model.Customer.Address from here even though they are all split out into individual properties?

I think you have two separate issues to address here. The first is how to get the stored procedure to return and bind all of the data into your EF complex type. There are a couple of articles here (blog) and here (CodeProject) that should be of help. Essentially, write your sproc to return multiple result sets, and a couple lines of code to advance to the next result set and read it into the context.

To get the mapping you want from AutoMapper, you need to add maps for each type that you use as a property, too (eg, a map from your EF 'Address' type to your POCO 'Address' type).

This is actually fairly trivial, but you might have to set up some AutoMapper configuration. Your stored procedure can really only return flattened results, but you can still pass all the fields for all the relationships. If you want to go almost configuration free, you really just need to have your stored procedure return the property names in a flattened format. For example, AddressCity instead of something like City . If the object you're mapping from has a property like AddressCity , and the object you're mapping to has a navigation property Address , which itself has a property City , AutoMapper will automatically fill this in appropriately. If you don't want to or can't change the column names returned by the stored procedure, then you just have to configure AutoMapper:

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

If you have more complex scenarios like an enumerable navigation property, this method won't really work. Your best bet in that case is to do it in multiple steps, such that you use one stored procedure to retrieve the primary object and everything that can be flattened with it. With that, you map to your desired type. Then, use a separate stored procedure to look up the enumerable item based on the primary object's id or something, and map those to the enumerable property on the previously mapped object.

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

    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM