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