Search code examples
.netasp.net-mvcdapper

Dapper - multi-mapping APIs ensure you set the splitOn


For the code shown below, I am getting the following error message.

*** Error ***

When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id (Parameter 'splitOn')

From what i understand i am passing the splitOn parameter am i missing something obvious?

Code


 using (var connection = new Microsoft.Data.SqlClient.SqlConnection(connectionString))
                {
                    connection.Open();
                    LookupTables = connection.Query<LookupTable, List<LookupValue>, LookupTable>("exec [lookups].[SelAllLookupTable]", 
                        (lookuptable, lookupvalue) => {
                            lookuptable.LookUpValues = lookupvalue.ToList<LookupValue>(); 
                            return lookuptable;
                        }, splitOn: "LookupValueID").ToList();
                }


Update

Stored Proc Code


ALTER PROCEDURE [lookups].[SelAllLookupTable]
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT lt.[LookupTableID],
        lt.[GUID],
        lt.[CreatedBy],
        lt.[CreatedOn],
        lt.[UpdatedBy],
        lt.[UpdatedOn],
        lt.[ChangeReason],
        lt.[Name],
        lt.[Description],
        lv.[LookupValueID],
        lv.[LookupTableID],
        lv.[GUID],
        lv.[CreatedBy],
        lv.[CreatedOn],
        lv.[UpdatedBy],
        lv.[UpdatedOn],
        lv.[ChangeReason],
        lv.[Description],
        lv.[DisplayOrderNo]
    FROM lookups.LookupTable lt
    LEFT JOIN lookups.LookupValue lv ON lt.LookupTableID = lv.LookupTableID
END



Solution

  • I don't have your data, but I think you should change your code in this way

    // Where we store the distinct LookUpTable found by the query
    Dictionary<int, LookUpTable> result = new Dictionary<int, LookUpTable>();
    
    using (var connection = new Microsoft.Data.SqlClient.SqlConnection(connectionString))
    {
        connection.Open();
        var LookupTables = connection.Query<LookupTable, LookUpValue, LookupTable>("exec [lookups].[SelAllLookupTable]",
            (lkt, lkv) =>
            {
                // Check if we have the instance already in the dictionary
                // add it if not, 
                if (!result.ContainsKey(lkt.LookUpTableID))
                   result.Add(lkt.LookUpTableID, lkt);
                result[lkt.LookupTableID].LookUpValues.Add(lkv);
                return lkt;
            }, splitOn: "LookupValueID").ToList();
    }
    return result.Values.ToList();
    

    Dapper will not build and pass a list of LookUpValue directly, but it will iterate over the records creating the objects from each returned record and passing these objects one by one to the lambda expression.
    Your lambda expression could use a Dictionary to store each LookupTable object received and add the LookupValue object to its property. When Dapper ends its iteration over the record your objects are stored in the Values collection of the dictionary

    Of course this assumes that your class LookUpTable has a List<LookUpValue> property

    public class LookupTable
    {
        public int LookupValueID { get; set; }
        .... other properties ....
        public List<LookUpValue> LookUpValues {get;set} = new List<LookUpValue>();
    }
    

    Notice how the property creates the list at initialization to avoid complex logic in the Dapper lambda expression.