SQLite.NET - System.NotSupportedException: Unable to compile: parameter

I am using SQLite.NET Async ( http://www.nuget.org/packages/SQLite.Net.Async-PCL/ ) in a Xamarin iOS project, but I have a problem using the query table predicate.

Anytime I use the Get method below it is very simple, I get an exception that the expression cannot be compiled, System.NotSupportedException: cannot compile: parameter.

However, if I use a low-level query, as in the GetQuery method, it works fine. Is there something that I am doing wrong in my table definition or method that prevents sqlite.net from compiling the expression?

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

Edit # 1: The problem seems to be related to the fact that my methods are general. If I change them to a model-specific "connection.Table <EventDataModel> .Where (..." it works. Will not common methods be used?

Edit # 2: I added a β€œclass” constraint to T to accommodate the existing limitations of IDataModel, new (), and this seems to fix the problem ... Does this make sense?

+7
source share
3 answers

It makes sense that adding a class constraint will solve the problem.

When you write:

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

You do not see this, but the compiler will insert a cast between item and item.Id

That is, what the compiler actually writes:

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

This listing is inserted because it is necessary if T is a value type.

It is easy to imagine that the query provider for SQLite.net does not correctly process the entered data, since this is not trivial.

Adding a class constraint allows the compiler to avoid inserting this cast, which leads to a simpler expression that the SQLite.net query provider can apparently translate correctly.

+11
source share

The problem, I suppose, would be that the compiler does not know that the element has a property identifier.

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

You can create an interface with an id field and use where T:

 public interface ITable { int Id { get; set; } } public virtual async Task<T> Get(int id) where T : ITable { ... 

Then you should probably just use FindAsync:

 public virtual async Task<T> Get(int id) { var connection = await GetConnection(); return await connection.FindAsync<T>(id); } 
+4
source share

For those who are interested in what constitutes a class constraint for T , the following is the signature of the Task<T> GetAsync<T>(int id) where T : class, IDbModel, new();

0
source share

All Articles