Reputation: 3342
I am working on a program to manage my personal finances. I have an SQLite database that is storing all of the data, and I am able to load/save accounts, bills, payments, etc.
What I am looking to do, is load the account name of the associated account based on the PayeeId
of each Payment
. I know how to achieve this using SQL, but my data is set up using repositories. For example, I load the Payments
by calling
var payments = await _paymentsRepository.LoadAllAsync();
And the LoadAllAsync()
method is in RepositoryBase
class, and looks like so:
public async Task<IEnumerable<TTable>> LoadAllAsync()
{
var query = _sqliteService.Conn.Table<TTable>();
var array = (await query.ToListAsync()).ToArray();
return array;
}
and is declared in the IPaymentsRepository
interface like so:
Task<IEnumerable<Payment>> LoadAllAsync();
Each Payment
object has a PayeeId
property that links to the Payee
for that Payment
. The Payment
itself doesn't store any of the other information about the Payee
, but I would like to be able to load the PayeeName
property to display with the Payment
information. Is there a simple way to do this, or will I have to create a separate ViewModel
to store the "hybrid" data containing both the Payment
information as well as the Payee
information?
EDIT
I know I can accomplish this using an extra class, for example PaymentInfo
or something, and store both the Payment
and Payee
data, then access it like this: PaymentInfo.Payment.PaymentAmount
or PaymentInfo.Payee.PayeeName
, but I would have to load them in two separate queries. While this is certainly possible, I am hoping for a solution that can be accomplished in one query, which is why I am looking at using a JOIN
. If I need to, I will just use LINQ, but my question is whether or not this is possible using the repository setup that I currently have.
EDIT 2
Here is the repository code. I have tried to only include the relevant pieces. Each table has its own repository. Here is the signature of the PaymentsRepository
:
public class PaymentsRepository : RepositoryBase<Payment, int>, IPaymentsRepository
{
}
The RepositoryBase<>
looks like this:
public abstract class RepositoryBase<TTable, TKey> : IRepository<TTable, TKey>
where TTable : IKeyedTable<TKey>, new()
{
protected readonly ISqliteService SqliteService;
protected RepositoryBase(ISqliteService sqlLiteService)
{
SqliteService = sqlLiteService;
}
public async Task<IEnumerable<TTable>> LoadAllAsync()
{
var query = SqliteService.Conn.Table<TTable>();
var array = (await query.ToListAsync()).ToArray();
return array;
}
......
}
The IRepository
interface:
interface IRepository<TTable, in TKey>
where TTable : IKeyedTable<TKey>, new()
{
Task<TTable> LoadByIdAsync(TKey id);
Task<IEnumerable<TTable>> LoadAllAsync();
Task InsertAsync(TTable item);
Task UpdateAsync(TTable item);
Task DeleteAsync(TTable item);
AsyncTableQuery<TTable> Query();
}
And the ISqliteService
:
public interface ISqliteService
{
SQLiteAsyncConnection Conn { get; }
Task<object> ClearLocalDb();
void Reconnect();
}
Everything is ultimately queried against that SQLiteAsyncConnection
property, using the built-in SQLite methods. For example, in the LoadAllAsync()
function, var query = _sqliteService.Conn.Table<TTable>();
uses this:
public AsyncTableQuery<T> Table<T> ()
where T : new ()
{
//
// This isn't async as the underlying connection doesn't go out to the database
// until the query is performed. The Async methods are on the query iteself.
//
var conn = GetConnection ();
return new AsyncTableQuery<T> (conn.Table<T> ());
}
which is located in SQLiteAsync.cs
Upvotes: 0
Views: 1673
Reputation: 593
I think you can get IQueryable<Payment>
and IQueryable<Payee>
, join them in LINQ, and then call .ToArray() on the result.
It will build up the query and perform it only when you actually access the data (in this case, on the ToArray() call). I believe this should generate a single query.
Upvotes: 0
Reputation: 3342
I wasn't able to figure out a way to directly query two different tables using LINQ, but I got things working with a "hybrid" class. I just created a PaymentInfo
class that has a Payment
property and a Payee
property, which point to the relevant data. I added a method to my PaymentsRepository
that looks like this:
public async Task<IEnumerable<PaymentInfo>> LoadAllPaymentInfoAsync()
{
var payments = await SqliteService.Conn.Table<Payment>().ToListAsync();
var payees = await SqliteService.Conn.Table<Payee>().ToListAsync();
var query = from p1 in payments
join p2 in payees on p1.PayeeId equals p2.Id
select new PaymentInfo() {Payment = p1, Payee = p2};
return query;
}
I am sure that this is not necessarily the best way to accomplish this, but I thought I would share it here in case anyone comes across this page looking to do what I did.
Upvotes: 1