How do I join 2 tables in ServiceStack OrmLite and select both classes?
Asked Answered
S

1

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.

Sikang answered 24/5, 2016 at 14:23 Comment(0)
P
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
}
Piscine answered 24/5, 2016 at 17:38 Comment(14)
"No, there was no way, but yes, there is now" - now that's an answer! :D Thanks very much.Sikang
Now, if we could just continue to add to the SQL query after the join, using both tables, e.g. something like db.From<Customer>().Join<Customer, CustomerAddress>().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)Sikang
You can add an expression in the Join() and use Where<T1,T2>((t1,t2) => ...) laterPiscine
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?Sikang
No you can't change what it returns, it's just a JOIN condition that maps to a normal SQL INNER JOIN conditionPiscine
Then how do I use the Where<T1,T2> ? .Where() seem to have only one type parameter and it's the same one as in the original From ("Customer" in this example).Sikang
@EM See this SqlExpression for a Where examplePiscine
thanks, that works! (Even though I don't see Where<T1, T2> 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.Sikang
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 , you can also select an array of columns from multiple tables.Piscine
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.Sikang
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<string>(db.From<DeptEmployee>().Join<Department2>().Select(new[] { "Name" })) or db.Column<string>(db.From<DeptEmployee>().Join<Department2>().Select<DeptEmployee,Department2>((e,d) => new { d.Name })) you can also fetch dynamic results by selecting a List<object> or Dictionary<string,object> Types.Piscine
Yes, good point, I've posted a new question: #37442901 .Column() is not enough, because I want to select some columns from each of the joined tables.Sikang
Hi, is there a way to do a Distinct on a SelectMulti, at the SQL level, not after the query execution?Closegrained
Anytime I read anything with static methods on primitive .NET types, I know who's content I am reading. :)Levanter

© 2022 - 2024 — McMap. All rights reserved.