【问题标题】:SQLite.NET - System.NotSupportedException: Cannot compile: ParameterSQLite.NET - System.NotSupportedException:无法编译:参数
【发布时间】:2014-02-22 22:17:25
【问题描述】:

我在 Xamarin iOS 项目中使用 SQLite.NET Async (http://www.nuget.org/packages/SQLite.Net.Async-PCL/),但在使用表谓词查询时遇到问题。

每当我使用下面的Get方法时,很简单,我收到一个表达式无法编译的异常,System.NotSupportedException:无法编译:参数。

但是,如果我使用低级别查询,例如 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(...” 它可以工作。通用方法会不起作用吗?

编辑#2: 我在 T 上添加了一个“类”约束,以配合现有的“IDataModel,new()”约束,这似乎解决了问题......这有意义吗?

【问题讨论】:

  • 必须缺少某些东西 - 由于 Id 未在类型 T 上定义,当前代码无法编译。
  • 是的,我省略了一些代码,因为实际代码要复杂得多。我已经更新了。
  • 是的,约束非常有意义。您可以通过使用不包含 Id 字段的类运行 GetQuery 函数来尝试此操作。基于 LINQ 的函数是强类型的,而查询不是(where 子句部分)。在桌面/服务器应用程序上,您可以使用动态参数而不是通用参数,但就像查询一样,您可能会遇到运行时异常。
  • 关于查询的另一件事是,如果你想让它通用,你不能使用硬编码的“FROM Event”,你必须用 TableAttribute 的值替换它([Table (“事件”)])。
  • “添加类约束”是什么意思?

标签: c# xamarin.ios xamarin .net-4.5 sqlite.net


【解决方案1】:

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

您看不到它,但编译器会在 itemitem.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 查询提供程序显然可以正确翻译。

【讨论】:

  • public T GetItem&lt;T&gt;(int id) where T : BL.Contracts.IBusinessEntity, new(){ lock (locker) { return database.Table&lt;T&gt;().FirstOrDefault(x =&gt; x.ID == id); } } 我的似乎无法修复
  • @NorCalKnockOut:您是否在 class 上添加了 T 约束?如果这不能解决问题,请再问一个问题。
【解决方案2】:

我认为问题在于编译器不知道该项目具有属性 Id。

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

您可以使用 Id 字段创建接口并使用 where 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);
}

【讨论】:

  • 我用更多的上下文更新了代码。我的数据模型正在实现一个具有 Id 字段的接口。 FindAsync 可以在这种简单的情况下工作,但不能在同样失败的更复杂的情况下工作。
  • 我遇到了与原始海报类似的问题,并将我的LoadByIdAsync() 修改为调用Find&lt;T&gt;() 而不是FirstOrDefaultAsync,一切都很好。
【解决方案3】:

对于那些想知道T 上的class 约束是什么的人,以下是方法签名 Task&lt;T&gt; GetAsync&lt;T&gt;(int id) where T : class, IDbModel, new();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 2013-10-08
    • 2016-08-06
    • 2020-01-21
    相关资源
    最近更新 更多