I am wondering if there is a way to write a generic repository for my Xamarin project versus writing a different Repository for each entity in my object. The Xamarin Tasky P
This is an old question but here is my implementation.
I´m using async connections as they provide better performance in mobile projects. The nuggets I installed are Sqlite.Net-PCL/SQLite.Net.Async-PCL on the Core project and the corresponding nuget on the Android project.
My Repository looks like this:
using System;
using System.Collections.Generic;
using Core.Models;
using SQLite.Net;
using System.Linq;
using SQLite.Net.Async;
using System.Threading.Tasks;
using System.Linq.Expressions;
namespace Core.Managers
{
public interface IRepository where T : class, new()
{
Task> Get();
Task Get(int id);
Task> Get(Expression> predicate = null, Expression> orderBy = null);
Task Get(Expression> predicate);
AsyncTableQuery AsQueryable();
Task Insert(T entity);
Task Update(T entity);
Task Delete(T entity);
}
public class Repository : IRepository where T : class, new()
{
private SQLiteAsyncConnection db;
public Repository(SQLiteAsyncConnection db)
{
this.db = db;
}
public AsyncTableQuery AsQueryable() =>
db.Table();
public async Task> Get() =>
await db.Table().ToListAsync();
public async Task> Get(Expression> predicate = null, Expression> orderBy = null)
{
var query = db.Table();
if (predicate != null)
query = query.Where(predicate);
if (orderBy != null)
query = query.OrderBy(orderBy);
return await query.ToListAsync();
}
public async Task Get(int id) =>
await db.FindAsync(id);
public async Task Get(Expression> predicate) =>
await db.FindAsync(predicate);
public async Task Insert(T entity) =>
await db.InsertAsync(entity);
public async Task Update(T entity) =>
await db.UpdateAsync(entity);
public async Task Delete(T entity) =>
await db.DeleteAsync(entity);
}
}
Some examples on how to use it:
var connection = new SQLiteAsyncConnection(() => sqlite.GetConnectionWithLock());
await connection.CreateTablesAsync();
IRepository stockRepo = new Repository(connection);
IRepository ingredientRepo = new Repository(connection);
var stock1 = new Stock {
IngredientId = 1,
DaysToExpire = 3,
EntryDate = DateTime.Now,
Location = StockLocations.Fridge,
MeasureUnit = MeasureUnits.Liter,
Price = 5.50m,
ProductName = "Leche Auchan",
Quantity = 3,
Picture = "test.jpg",
Family = IngredientFamilies.Dairy
};
var stockId = await stockRepo.Insert(stock1);
var all = await stockRepo.Get();
var single = await stockRepo.Get(72);
var search = await stockRepo.Get(x => x.ProductName.StartsWith("something"));
var orderedSearch = await stockRepo.Get(predicate: x => x.DaysToExpire < 4, orderBy: x => x.EntryDate);
If the Repository does not meet your query needs, you can use AsQueryable():
public async Task> Search(string searchQuery, StockLocations location, IngredientFamilies family)
{
var query = stockRepo.AsQueryable();
if (!string.IsNullOrEmpty(searchQuery))
{
query = query.Where(x => x.ProductName.Contains(searchQuery) || x.Barcode.StartsWith(searchQuery));
}
if (location != StockLocations.All)
{
query = query.Where(x => x.Location == location);
}
if (family != IngredientFamilies.All)
{
query = query.Where(x => x.Family == family);
}
return await query.OrderBy(x => x.ExpirationDays).ToListAsync();
}