6

I'd like to do a simple SQL join in ServiceStack OrmLite and get both tables as the corresponding .NET objects.

In LINQ-to-Entities it would be something like this:

Claim.Join(Policy, c => c.PolicyId, p => p.Id, (c, p) => new { Claim = c, Policy = p })

This would give me an anonymous type with Claim and Policy properties.

I've looked at the OrmLite Advanced Join Example, but that only selects some of the properties of each type into a 3rd type of object (FullCustomerInfo). I don't want to repeat all my properties in another object, I just want to use the existing objects.

In reality, the query can be much more complex, e.g.

Claim.Join(Policy, c => c.PolicyId, p => p.Id, (c, p) => new { Claim = c, Policy = p })
    .Where(o => o.Policy.Something > o.Claim.Something)
    .Select(o => o.Claim.SomethingElse)

... etc., but even if OrmLite could just do the join in SQL and I had to do the rest in memory it would be a good start.

Bjarki Heiðar
  • 3,117
  • 6
  • 27
  • 40
EM0
  • 5,369
  • 7
  • 51
  • 85

1 Answers1

13

This wasn't available in OrmLite previously but as it's a nice feature to have I've just added support for SelectMulti<T,T2,..> and SelectMultiAsync in this commit which now lets you read up to 7 of your joined tables from a single query.

So to use OrmLite's Advanced Join Example you can construct a typed Join Query with:

var q = db.From<Customer>()
    .Join<Customer, CustomerAddress>()
    .Join<Customer, Order>();

Then use the SelectMulti APIs to populate the tables you're interested in, e.g:

var results = db.SelectMulti<Customer, CustomerAddress, Order>(q);

Which will return a List<Tuple<T,T2,T3>> giving you typed access to your populated POCOs from tables in your joined query.

If preferred, there's also an async version:

var results = await db.SelectMultiAsync<Customer, CustomerAddress, Order>(q);

The new SelectMulti APIs are available from v4.0.57 that's now available on MyGet.

Create Typed Queries in OrmLite and Execute them in Dapper

An alternative is to use a combination of OrmLite to create the typed query using its built-in Reference Conventions and then use OrmLite's embedded version of Dapper to Query Multiple result-sets into your existing POCO Types.

To start with create your Typed Query Expression and have it return all fields from all tables with:

var q = db.From<Customer>()
    .Join<Customer, CustomerAddress>()
    .Join<Customer, Order>()
    .Select("*");

Then pass the generated SQL from the above typed SQL Expression into Dapper's Query Multiple feature to read the results into a List of Tuples of the different joined tables:

using (var multi = db.QueryMultiple(q.ToSelectStatement()))
{
    var results = multi.Read<Customer, CustomerAddress, Order, 
        Tuple<Customer,CustomerAddress,Order>>(Tuple.Create).ToList();

    foreach (var tuple in results)
    {
        "Customer:".Print();
        tuple.Item1.PrintDump();

        "Customer Address:".Print();
        tuple.Item2.PrintDump();

        "Order:".Print();
        tuple.Item3.PrintDump();
    }
}

Which prints out something like:

Customer:
{
    Id: 1,
    Name: Customer 1
}
Customer Address:
{
    Id: 1,
    CustomerId: 1,
    AddressLine1: 1 Australia Street,
}
Order:
{
    Id: 1,
    CustomerId: 1,
    LineItem: Line 1,
    Qty: 1,
    Cost: 1.99
}    
Customer:
{
    Id: 1,
    Name: Customer 1
}
Customer Address:
{
    Id: 1,
    CustomerId: 1,
    AddressLine1: 1 Australia Street,
}
Order:
{
    Id: 2,
    CustomerId: 1,
    LineItem: Line 2,
    Qty: 2,
    Cost: 2.99
}
mythz
  • 141,670
  • 29
  • 246
  • 390
  • 2
    "No, there was no way, but yes, there is now" - now that's an answer! :D Thanks very much. – EM0 May 25 '16 at 13:51
  • Now, if we could just continue to add to the SQL query after the join, using both tables, e.g. something like `db.From().Join().Where(t => t.Item1.Name.Length > t.Item2.City.Length).Select(t => new[] { t.Item1.Name, t.Item2.City });` (silly example, but you get the idea) – EM0 May 25 '16 at 13:52
  • 1
    You can add an expression in the Join() and use Where((t1,t2) => ...) later – mythz May 25 '16 at 13:59
  • I can't seem to figure out how to add an expression to Join() to control what it returns. The Func<> it takes returns bool, so I presume that's just "JOIN ON ..." expression. Could you give an example? – EM0 May 25 '16 at 14:11
  • No you can't change what it returns, it's just a JOIN condition that maps to a normal SQL INNER JOIN condition – mythz May 25 '16 at 14:13
  • Then how do I use the `Where` ? .Where() seem to have only one type parameter and it's the same one as in the original From ("Customer" in this example). – EM0 May 25 '16 at 14:18
  • @EM See this [SqlExpression for a Where example](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/cf17ebc03d574d517a49fe3e4727ec877c88c950/tests/ServiceStack.OrmLite.Tests/Expression/ComplexJoinTests.cs#L185) – mythz May 25 '16 at 14:24
  • thanks, that works! (Even though I don't see Where in IntelliSense somehow...) What about selecting some of the columns (from both tables) in the SQL - is that possible? I adding `.Select(new string[] { "Name", "City"})` after the .Join(), but that seems to have no effect - I still get back the original two classes. – EM0 May 25 '16 at 14:45
  • Not sure what you mean, the only way to select which fields are returned is with `.Select()` APIs. But yeah you can use Select to [return an anonymous type from multiple tables](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/cf17ebc03d574d517a49fe3e4727ec877c88c950/tests/ServiceStack.OrmLite.Tests/Expression/ComplexJoinTests.cs#L263) , you can also [select an array of columns from multiple tables](https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/AutoQueryTests.cs#L259). – mythz May 25 '16 at 14:51
  • I mean, in the `Does_only_populate_Select_fields_wildcard example`, how would I get `Department2.Name` ? `results` is a list of `DeptEmployee` objects, so it doesn't contain any properties of `Department2`. – EM0 May 25 '16 at 15:32
  • It's better if you open a new question as comments isn't the right place for code, but you can fetch it with: `db.Column(db.From().Join().Select(new[] { "Name" }))` or `db.Column(db.From().Join().Select((e,d) => new { d.Name }))` you can also [fetch dynamic results](https://github.com/ServiceStack/ServiceStack.OrmLite#dynamic-result-sets) by selecting a `List` or `Dictionary` Types. – mythz May 25 '16 at 15:51
  • Yes, good point, I've posted a new question: http://stackoverflow.com/questions/37442401/ormlite-query-to-select-some-of-the-columns-from-each-of-2-joined-tables `.Column()` is not enough, because I want to select some columns from each of the joined tables. – EM0 May 25 '16 at 16:04
  • Hi, is there a way to do a Distinct on a SelectMulti, at the SQL level, not after the query execution? – richardwhatever Oct 12 '18 at 07:56
  • Anytime I read anything with static methods on primitive .NET types, I know who's content I am reading. :) – Paul Knopf Apr 09 '19 at 19:35