dappermulti-mapping

Multimapping in Dapper Without Custom SQL


Is there a way to use multimapping in Dapper in a generic way, without using custom SQL embedded in C# code?

See for example Correct use of Multimapping in Dapper

Is there a generic way to query the data from 2 related entities, where common fields are determined automatically for join?


Solution

  • Don't do this. Don't even think this way! Databases are long lasting and normalized. Objects are perishable and frequently denormalized, and transitioning between the two is something to do thoughtfully, when you're writing your SQL. This is really not a step to automate. Long, painful experience has convinced many of us that database abstractions (tables and joins) should not just be sucked into (or generated out of) code. If you're not yet convinced, then use an established ORM.

    If, on the other hand, you absolutely want to be in control of your SQL, but its the "embedding" in string literals in C# that bugs you, then I couldn't agree more. Can I suggest QueryFirst, a visual studio extension that generates the C# wrapper for your queries. Your SQL stays in a real SQL file, syntax validated, DB references checked, and at each save, QueryFirst generates a wrapper class with Execute() methods, and a POCO for the results.

    By multi-mapping, I presume you want to fill a graph of nested objects. A nice way to do this is to use one QueryFirst .sql per class in your graph, then in the partial class of the parent, add a List of children. (QueryFirst generated POCOs are split across 2 partial classes, you control one of them, the tool generates the other.)

    So, for a graph of Customers and their orders... In the parent sql

    select * from customers where name like @custName
    

    The child sql

    select * from orders where customerId = @customerId
    

    In the parent partial class, for eager loading...

        public List<Orders> orders;
        public void OnLoad()
        {
            orders = new getOrders().Execute(customerId); // property of the parent POCO
        }
    

    or for lazy loading...

        private List<Orders> _orders;
        public List<Orders> orders
        {
            get
            {                
                return _orders ?? _orders = new GetOrders().Execute(customerId);                
            }
        }
    

    5 lines of code, not counting brackets, and you have a nested graph, lazy loaded or eager loaded as you prefer, the interface discoverable in code (intellisense for the input parameter and result). Their might be hundreds of columns in those tables, whose names you will never need to re-type, and whose datatypes are going to flow transparently into your C#.

    Clean separation of responsibilities. Total control. Disclaimer : I wrote QueryFirst :-)