Xamarin 项目中 SQLite-Net 的通用存储库

Posted

技术标签:

【中文标题】Xamarin 项目中 SQLite-Net 的通用存储库【英文标题】:Generic Repository for SQLite-Net in Xamarin Project 【发布时间】:2015-05-17 00:10:48 【问题描述】:

我想知道是否有一种方法可以为我的 Xamarin 项目编写通用存储库,而不是为我的对象中的每个实体编写不同的存储库。 Xamarin Tasky Pro 示例有一个用于任务实体的存储库,因为这是它拥有的唯一实体。

在我自己的项目中,我有多个实体,所以我的问题是如何制作 以下 Customer Repository 成为通用的,以便 ProductManager、EmployeeManager 等可以使用它。如果您知道示例或博客文章,请指出正确的方向

namespace App.DataLayer

    public class CustomerRepository
    
        private ProntoDatabase _db = null;
        protected static string DbLocation;
        protected static CustomerRepository Me;

        static CustomerRepository()
        
            Me = new CustomerRepository();
        

        protected CustomerRepository()
        
            //set the db location;
            DbLocation = DatabaseFilePath;

            //instantiate the database
            _db = new ProntoDatabase(DbLocation);
        


        public static string DatabaseFilePath
        
            get
            
                const string sqliteFilename = "CustomerDB.db3";
                var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                var path = Path.Combine(libraryPath, sqliteFilename);
                return path;
            
        


        // CRUD (Create, Read, Update and Delete) methods

        public static Customer GetCustomer(int id)
        
            return Me._db.GetItem<Customer>(id);
        

        public static IEnumerable<Customer> GetCustomers()
        
            return Me._db.GetItems<Customer>();
        

        public static int SaveCustomer(Customer item)
        
            return Me._db.SaveItem(item);
        

        public static int DeleteCustomer(int id)
        
            return Me._db.DeleteItem<Customer>(id);
        
    

【问题讨论】:

您现在必须在 SQLiteConnectionWithLock 和 SQLiteConnection 构造函数中传入 ISQlitePlatform 的实现。正确的平台实现会自动添加到项目中。 【参考方案1】:

这是一个老问题,但这是我的实现。

我正在使用异步连接,因为它们在移动项目中提供了更好的性能。我安装的nuggets在Core项目上是Sqlite.Net-PCL/SQLite.Net.Async-PCL,在android项目上是对应的nuget。

我的存储库如下所示:

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<T> where T : class, new()
    
        Task<List<T>> Get();
        Task<T> Get(int id);
        Task<List<T>> Get<TValue>(Expression<Func<T, bool>> predicate = null, Expression<Func<T, TValue>> orderBy = null);
        Task<T> Get(Expression<Func<T, bool>> predicate);
        AsyncTableQuery<T> AsQueryable();
        Task<int> Insert(T entity);
        Task<int> Update(T entity);
        Task<int> Delete(T entity);
    

    public class Repository<T> : IRepository<T> where T : class, new()
    
        private SQLiteAsyncConnection db;

        public Repository(SQLiteAsyncConnection db)
        
            this.db = db;
        

        public AsyncTableQuery<T> AsQueryable() => 
            db.Table<T>();

        public async Task<List<T>> Get() => 
            await db.Table<T>().ToListAsync();

        public async Task<List<T>> Get<TValue>(Expression<Func<T, bool>> predicate = null, Expression<Func<T, TValue>> orderBy = null)
        
            var query = db.Table<T>();

            if (predicate != null)
                query = query.Where(predicate);

            if (orderBy != null)
                query = query.OrderBy<TValue>(orderBy);

            return await query.ToListAsync();
        

        public async Task<T> Get(int id) => 
             await db.FindAsync<T>(id);

        public async Task<T> Get(Expression<Func<T, bool>> predicate) =>
            await db.FindAsync<T>(predicate);

        public async Task<int> Insert(T entity) => 
             await db.InsertAsync(entity);

        public async Task<int> Update(T entity) =>
             await db.UpdateAsync(entity);

        public async Task<int> Delete(T entity) =>
             await db.DeleteAsync(entity);
    

一些使用方法的例子:

var connection = new SQLiteAsyncConnection(() => sqlite.GetConnectionWithLock());
await connection.CreateTablesAsync<Ingredient, Stock>();

IRepository<Stock> stockRepo = new Repository<Stock>(connection);
IRepository<Ingredient> ingredientRepo = new Repository<Ingredient>(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);

如果 Repository 不能满足你的查询需求,你可以使用 AsQueryable():

public async Task<List<Stock>> 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();

【讨论】:

哇,太好了。这正是我正在寻找的。我会使用它,看看我是否可以用它来替换我的 7 个不同的存储库 所有者有未列出的Sqlite.Net.Platform.XamarinAndroid 包。这可能意味着该软件包已被弃用或不应再使用。 LINK 没有“public class Repository : IRepository where T : class, new()”是如何工作的 @PEO 你能找到解决方案吗?我的编译器还抱怨“public class Repository : IRepository where T : new()”。错误消息是:“类型'T'必须是引用类型才能在泛型类型或方法'IRepository'中用作参数'T'” 在 .只是你的类应该是“public class Repository : IRepository where T: class, new()”【参考方案2】:

我在 unity IOC 的帮助下实现如下,我的项目包括 PCL、Xamarin Android 和 Xamarin ios 项目

使用主键定义基本模型

public class BaseModel

    [PrimaryKey, AutoIncrement]
    public int Id  get; set; 

定义一个通用的基础存储库,如下所示

public interface IBaseRepository<T> : IDisposable
    where T :BaseModel, new()
    
        List<T> GetItems();

        T GetItem(int id);

        int GetItemsCount();

        int SaveItem(T item);

        int SaveAllItem(IEnumerable<T> items);
    


public class BaseRepository<T> : BaseRepository<T> where T : BaseModel, new()
    
        private static readonly object locker = new object();
        protected SQLiteConnection DatabaseConnection;
        public BaseRepository(string dbPath)
        
            DatabaseConnection = new SQLiteConnection(dbPath);
            DatabaseConnection.CreateTable<T>();
        

        public List<T> GetItems()
        
            lock (locker)
            
                return DatabaseConnection.Table<T>().ToList();
            
        

        public int GetItemsCount()
        
            lock (locker)
            
                return DatabaseConnection.Table<T>().Count();
            
        

        public T GetItem(int id)
        
            lock (locker)
            
                return DatabaseConnection.Table<T>().Where(i => i.Id == id).FirstOrDefault();
            
        

        public int SaveItem(T item)
        
            lock (locker)
            
                if (item.Id != 0)
                
                    return DatabaseConnection.Update(item);
                
                else
                
                    return DatabaseConnection.Insert(item);
                
            
        


    

定义两个继承自 BaseModel 的示例类

public class Entity1 : BaseModel
    
        public int ItemName
        
            get;
            set;
        
    


public class Entity2 : BaseModel

    public int Description
    
        get;
        set;
    



public static UnityContainer Container  get; private set; 

    public static void InitializeUnityContainer()
    
        if (Container == null)
            Container = new UnityContainer();
    

注册

Container.RegisterInstance<IBaseRepository<Entity1>>(new BaseRepository<Entity1>(DatabasePath));
    Container.RegisterInstance<IBaseRepository<Entity2>>(new BaseRepository<Entity2>(DatabasePath));

这样解决

using (var repo1 = App.Container.Resolve<IBaseRepository<Entity2>>())



【讨论】:

以上是关于Xamarin 项目中 SQLite-Net 的通用存储库的主要内容,如果未能解决你的问题,请参考以下文章

C#使用sqlite-net搭建简易的ORM

SQLite-Net 扩展 - GetAllWithChildrenAsync 没有拉动一切

sqlite-net 更新方法是如何工作的?

使用带有 sqlite-net orm 的 SQLiteConnection.Delete(object) 命令后更改 sqlite 文件大小

如何在带有 ARM CPU 的 WinRT 设备中部署 SQLite?

DAO 中独特的通证经济