SQLite.NET - System.NotSupportedException: не удается скомпилировать: параметр

Я использую SQLite.NET Async ( http://www.nuget.org/packages/SQLite.Net.Async-PCL/) в проекте Xamarin iOS, но у меня возникла проблема с использованием запросов предикатов таблиц.

Каждый раз, когда я использую метод Get ниже, который очень прост, я получаю исключение, что выражение не может быть скомпилировано, System.NotSupportedException: Cannot compile: Parameter.

Однако, если я использую низкоуровневый запрос, как в методе GetQuery, он работает нормально. Есть ли что-то, что я делаю неправильно в определении моей таблицы или в методе, который мешает sqlite.net скомпилировать выражение?

public interface IDataModel
{
    [PrimaryKey, AutoIncrement]
    int Id { get; set; }
}

public class BaseDataModel : IDataModel
{
    [PrimaryKey]
    public virtual int Id { get; set; }
}

[Table("Event")]
public class EventDataModel : BaseDataModel
{
    public string Name { get; set; }
    public int OrganizationId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public bool Active { get; set; }
}

public class DataService<T> : IDataService<T> where T : IDataModel, new()
{

    public virtual async Task<T> Get(int id)
    {
        var connection = await GetConnection();

        return await connection.Table<T>()
                .Where(item => item.Id == id)
                .FirstOrDefaultAsync();
    }

    public virtual async Task<T> GetQuery(int id)
    {
        var connection = await GetConnection();

        return (await connection.QueryAsync<T>("SELECT * FROM Event WHERE Id = ?", id))
             .FirstOrDefault();
    }
}

Редактировать #1: Проблема, похоже, связана с тем, что мои методы являются общими. Если я изменю их на специфические для модели "connection.Table.Where (...", это сработает. Универсальные методы не сработают?

Edit # 2: я добавил ограничение 'class' на T, чтобы соответствовать существующим ограничениям 'IDataModel, new()', и это, похоже, решило проблему... Имеет ли это смысл?

2 ответа

Решение

Это имеет смысл, что добавление class ограничение решит проблему.

Когда вы пишете:

public virtual async Task<T> Get(int id)
    where T : IDataModel, new()
{
    var connection = await GetConnection();

    return await connection.Table<T>()
            .Where(item => item.Id == id)
            .FirstOrDefaultAsync();
}

Вы не видите его, но компилятор вставит приведение между item а также item.Id,

То есть, что на самом деле пишет компилятор:

public virtual async Task<T> Get(int id)
    where T : IDataModel, new()
{
    var connection = await GetConnection();

    return await connection.Table<T>()
            .Where(item => ((IDataModel)item).Id == id)
            .FirstOrDefaultAsync();
}

Это приведение вставлено, потому что это было бы необходимо, если T это тип значения

Легко представить, что поставщик запросов для SQLite.net не обрабатывает вставленное приведение правильно, поскольку это не тривиально.

Добавление class Ограничение позволяет компилятору избегать вставки этого приведения, что приводит к более простому выражению, которое поставщик запросов SQLite.net, по-видимому, может правильно перевести.

Я предполагаю, что проблема заключается в том, что компилятор не знает, что у элемента есть свойство Id.

return await connection.Table<T>()
        .Where(item => **item.Id** == id)
        .FirstOrDefaultAsync();

Вы можете создать интерфейс с полем Id и использовать где T:

public interface ITable
{
    int Id { get; set; }
}

    public virtual async Task<T> Get(int id) where T : ITable
    {
       ... 

Опять же, вы, вероятно, должны просто использовать FindAsync:

public virtual async Task<T> Get(int id)
{
    var connection = await GetConnection();

    return await connection.FindAsync<T>(id);
}

Для тех, кому интересно, что class ограничение на T есть, ниже следует подпись методаTask<T> GetAsync<T>(int id) where T : class, IDbModel, new();

Другие вопросы по тегам