有没有一种从 SQLite 存储库中加入表的简单方法?

Is there a simple way to JOIN tables from an SQLite repository?

我正在开发一个管理个人财务的程序。我有一个存储所有数据的 SQLite 数据库,我能够 load/save 账户、账单、付款等

我要做的是根据每个 PaymentPayeeId 加载关联帐户的帐户名称。我知道如何使用 SQL 实现此目的,但我的数据是使用存储库设置的。例如,我通过调用

加载 Payments
var payments = await _paymentsRepository.LoadAllAsync();

LoadAllAsync() 方法在 RepositoryBase class 中,看起来像这样:

public async Task<IEnumerable<TTable>> LoadAllAsync()
{
    var query = _sqliteService.Conn.Table<TTable>();
    var array = (await query.ToListAsync()).ToArray();

    return array;
}

并在 IPaymentsRepository 接口中声明如下:

Task<IEnumerable<Payment>> LoadAllAsync();

每个 Payment 对象都有一个 PayeeId 属性 链接到那个 PaymentPayeePayment 本身不存储有关 Payee 的任何其他信息,但我希望能够加载 PayeeName 属性 以显示 Payment 信息。有没有一种简单的方法可以做到这一点,或者我必须创建一个单独的 ViewModel 来存储包含 Payment 信息和 Payee 信息的 "hybrid" 数据?

编辑

我知道我可以使用额外的 class 来完成此操作,例如 PaymentInfo 或其他东西,并存储 PaymentPayee 数据,然后访问它像这样:PaymentInfo.Payment.PaymentAmountPaymentInfo.Payee.PayeeName,但我必须在两个单独的查询中加载它们。虽然这当然是可能的,但我希望有一个可以在一个查询中完成的解决方案,这就是我考虑使用 JOIN 的原因。如果需要,我将只使用 LINQ,但我的问题是使用我当前拥有的存储库设置是否可行。

编辑 2

这是存储库代码。我试图只包括相关的部分。每个 table 都有自己的存储库。这是 PaymentsRepository:

的签名
public class PaymentsRepository : RepositoryBase<Payment, int>, IPaymentsRepository
{
}

RepositoryBase<> 看起来像这样:

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;
    }
    ......
}

IRepository界面:

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();
}

ISqliteService:

public interface ISqliteService
{
    SQLiteAsyncConnection Conn { get; }
    Task<object> ClearLocalDb();
    void Reconnect();
}

最终会使用内置的 SQLite 方法针对 SQLiteAsyncConnection 属性 查询所有内容。例如,在 LoadAllAsync() 函数中,var query = _sqliteService.Conn.Table<TTable>(); 使用这个:

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> ());
}

位于SQLiteAsync.cs

我无法想出一种使用 LINQ 直接查询两个不同表的方法,但我可以使用 "hybrid" class。我刚刚创建了一个 PaymentInfo class,它有一个 Payment 属性 和一个 Payee 属性,它们指向相关数据。我向我的 PaymentsRepository 添加了一个方法,如下所示:

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;
}

我确信这不一定是完成此操作的最佳方法,但我想我会在这里分享它,以防有人看到此页面并希望执行我所做的事情。

我认为你可以获得 IQueryable<Payment>IQueryable<Payee>,在 LINQ 中加入它们,然后对结果调用 .ToArray()。

它将构建查询并仅在您实际访问数据时执行查询(在本例中,在调用 ToArray() 时)。 我相信这应该会生成一个查询。