sqlite-net icon indicating copy to clipboard operation
sqlite-net copied to clipboard

Select

Open jetonpeche opened this issue 1 year ago • 5 comments

Hello,

Can you add Select() plz ?

jetonpeche avatar Jul 13 '23 17:07 jetonpeche

what do you mean?

csm101 avatar Jul 14 '23 12:07 csm101

He means Connection.Table<Deez>().Select(x=>x.Nuts).Where(y=> y.Size == "Big"); I think.

Petrarca181 avatar Jul 14 '23 18:07 Petrarca181

hello,

Yes, like this but where before select like LINQ

jetonpeche avatar Jul 14 '23 19:07 jetonpeche

In sql Select comes first :)
But yes, having more LINQ integration would be nice.

Petrarca181 avatar Jul 14 '23 19:07 Petrarca181

I have written these extension methods to do a SelectAsync:

/// <summary>Method that returns a List of rows with selected properties</summary>
public static Task<List<TResult>> ToListAsync<T, TResult>(this AsyncTableQuery<T> asyncTableQuery, Func<T, TResult> selec) where T : new()
{
    return asyncTableQuery.ExecuteQueryAsync(selec, x => x.ToList());
}

/// <summary>Method that returns a row with selected properties that satisfies a condition</summary>
public static Task<TResult> FirstOrDefaultAsync<T, TResult>(this AsyncTableQuery<T> asyncTableQuery, Expression<Func<T, bool>> pred, Func<T, TResult> selec) where T : new()
{
    return asyncTableQuery.Where(pred).Take(1).ExecuteQueryAsync(selec, x => x.FirstOrDefault());
}

/// <summary>Method that returns a row with selected properties</summary>
public static Task<TResult> FirstOrDefaultAsync<T, TResult>(this AsyncTableQuery<T> asyncTableQuery, Func<T, TResult> selec) where T : new()
{
    return asyncTableQuery.Take(1).ExecuteQueryAsync(selec, x => x.FirstOrDefault());
}

private static Task<TEnd> ExecuteQueryAsync<T, TResult, TEnd>(this AsyncTableQuery<T> asyncTableQuery, Expression<Func<T, TResult>> selec, Func<IEnumerable<TResult>, TEnd> selec2) where T : new()
{
    var type = typeof(TResult);
    var _innerQuery = asyncTableQuery.GetPrivateField<TableQuery<T>>("_innerQuery");
    string selectionList = null;

    bool isPrimitive = type.IsPrimitiveType();
    if (isPrimitive) selectionList = selec.GetPropName();
    else
    {
        selectionList = string.Join(", ", type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Select(x => x.Name));
    }

    return _innerQuery.ReadAsync(conn =>
    {
        var command = (SQLiteCommand)_innerQuery.InvokeMethod("GenerateCommand", selectionList);
        return type.IsGenericType || isPrimitive ? selec2(command.ExecuteDeferredQuery<T>().Select(selec.Compile())) : selec2(command.ExecuteDeferredQuery<TResult>());
    });
}

private static Task<TResult> ReadAsync<T, TResult>(this TableQuery<T> _innerQuery, Func<SQLiteConnectionWithLock, TResult> read)
{
    return Task.Factory.StartNew(()=>
    {
        var connection = (SQLiteConnectionWithLock)_innerQuery.Connection;
        using (connection.Lock())
        {
            return read(connection);
        }
    }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}

public static T GetPrivateField<T>(this object obj, string fieldName)
{
    return (T)obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj);
}

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
    return typeof(T).GetTypeInfo().GetDeclaredMethod(methodName).Invoke(obj, args);
}

private static bool IsPrimitiveType(this Type type)
{
    return type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object;
}

public static string GetPropName<T,TResult>(this Expression<Func<T, TResult>> exp)
{
    var body = exp.Body as MemberExpression;
    if (body == null)
    {
        var ubody = (UnaryExpression)exp.Body;
        body = ubody.Operand as MemberExpression;
    }
    return body.Member.Name;
}

The usage from AsyncTableQuery is: var result = await connection.Table<Atraq>().Where(x => x.Id > 976).ToListAsync(x => new { x.Id, x.Certificate }); or var result = await connection.Table<Atraq>().Where(x => x.Id > 976).ToListAsync(x => new Val { Id = x.Id, Certificate = x.Certificate });

If the type you return it's a class, then it's maped to that class, else if it's a generic or primitive, it's mapped to table class and then to that type.

I wish more functionality of Linq integration like this could be added into the library. You can also check my AnyAsync() extension method here: https://github.com/praeclarum/sqlite-net/issues/1197#issue-1977503446

joseluisct avatar Nov 07 '23 00:11 joseluisct