How to LeftJoin to the same table twice using ServiceStack OrmLite?
Asked Answered
K

1

5

I have table structures that look like below:

table Tenant: Id[PK], etc
table Contact: Id[PK], FirstName, LastName etc
table Sale: Id[PK], TenantId[FK], SellerId[FK], BuyerId[FK], etc

SellerId is a FK to Contact.Id
BuyerId is a FK to Contact.Id
TenantId is a FK to Tenant.Id

I want to use OrmLite to generate SQL similar to below:

select  sale.*
    ,   buyer.FirstName 'BuyerFirstName'
    ,   buyer.LastName 'BuyerLastName'
    ,   seller.FirstName 'SellerFirstName'
    ,   seller.LastName 'SellerLastName'
from    sale
    left join
        contact seller
    on  sale.SellerId = seller.Id
    left join
        contact buyer
    on  sale.BuyerId = buyer.Id
where   tenantId = 'guid' -- this is being filtered at a global level

Because I want to have a strongly typed global filter to filter out result by tenantId (on database side) I have code looks like below

public List<TOut> Exec<TIn, TOut>(SqlExpression<TIn> exp) where TIn : IHaveTenantId
{
    exp.Where(x => x.TenantId == _tenantId);
    return _conn.Select<TOut>(exp);
}

The poco of Sale looks like below:

public class Sale : IHaveTenantId
{
    public Guid Id { get; set; }

    [ForeignKey(typeof(Contact), OnDelete = "CASCADE")]
    public Guid BuyerId { get; set; }

    [ForeignKey(typeof(Contact), OnDelete = "CASCADE")]
    public Guid SellerId { get; set; }

    //etc
}

And I'm trying to use strongly typed LeftJoin syntax like below:

public class SaleView
{
    public Guid Id { get; set; }
    public string BuyerFirstName { get; set; }
    public string SellerLastName { get; set; }
    //etc
}

var result = Exec<SaleView, Sale>(_conn
    .From<Sale>()
    .LeftJoin<Contact>((sale, seller) => sale.SellerId == seller.Id)
    .LeftJoin<Contact>((sale, buyer) => sale.BuyerId == buyer.Id));

I couldn't figure out how to join the same table multiple times and have an alias per join (e.g. left join contact as 'seller', hence I can select seller.FirstName, buyer.FirstName) and I don't want to use parameterised raw sql.

Is this possible at all with OrmLite?

Kira answered 19/1, 2015 at 7:6 Comment(0)
F
7

Support for typed JOIN aliases were added in v4.0.62, e.g:

var q = db.From<Sale>()
    .LeftJoin<ContactIssue>((s,c) => s.SellerId == c.Id, db.JoinAlias("seller"))
    .LeftJoin<ContactIssue>((s,c) => s.BuyerId == c.Id, db.JoinAlias("buyer"))
    .Select<Sale, ContactIssue>((s,c) => new {
        s,
        BuyerFirstName = Sql.JoinAlias(c.FirstName, "buyer"),
        BuyerLastName = Sql.JoinAlias(c.LastName, "buyer"),
        SellerFirstName = Sql.JoinAlias(c.FirstName, "seller"),
        SellerLastName = Sql.JoinAlias(c.LastName, "seller"),
    });

Prior to v4.0.62 you can continue to use a Typed SqlExpression with custom SQL for this, e.g:

var q = db.From<Sale>()
    .CustomJoin("LEFT JOIN Contact seller ON (Sale.SellerId = seller.Id)")
    .CustomJoin("LEFT JOIN Contact buyer ON (Sale.BuyerId = buyer.Id)")
    .Select(@"Sale.*
        , buyer.FirstName AS BuyerFirstName
        , buyer.LastName AS BuyerLastName
        , seller.FirstName AS SellerFirstName
        , seller.LastName AS SellerLastName");

The benefit of which is that it still leaves a Typed API where you can add additional filters like a global TenantId filter, e.g:

q.Where(x => x.TenantId == tenantId);

And then project it into your Custom POCO with:

var sales = db.Select<SaleView>(q);

The new CustomJoin API is available from v4.0.37+ that's now available on MyGet.

Ferric answered 19/1, 2015 at 19:4 Comment(7)
Great customer service! Thanks! I see it's on MyGet but how long will it be released on nuget?Kira
I tried 4.0.37 and it works great. However when I tried using CaptureSqlFilter there was never any records returned, as if no query was executed. I might dig a bit deeper and file a bug report later.Kira
@Kira CaptureSqlFilter is only a filter used to capture SQL, it doesn't actually execute them, i.e. it should always returns empty results. There's usually a release every 4-5 weeks, last one was recent so it will be a while.Ferric
@Ferric are there any plans to implement this into ServiceStack OrmLite?Bankhead
@Bankhead FYI, I've added support for a typed version for specifying custom join aliases available from v4.0.61 that's now available on MyGet.Ferric
@Ferric you champion! that's awesome!Bankhead
@Bankhead FYI, I managed to make it slightly nicer - now on MyGet.Ferric

© 2022 - 2024 — McMap. All rights reserved.