65.9K
CodeProject 正在变化。 阅读更多。
Home

Rednet DataAccess

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.38/5 (8投票s)

2016年5月27日

CPOL

6分钟阅读

viewsIcon

18568

downloadIcon

196

Rednet.DataAccess 是另一个用于数据处理的组件,它简化了您的使用。

引言

在我漫长的数据处理之旅中,我使用了很多数据访问库来简化开发,但有时这些库总是出现一些难以解决的问题,在大多数情况下,我总是需要编写 SQL 查询来实现数据检索和性能。

编写 SQL 查询的过程总是迫使我记住项目中不断变化的列名和表名。

背景

使用 Rednet.DataAccess,我们将定义类,其中一些属性带有指示我们对表和列偏好的特性,并以强类型方式执行 CRUD 操作和数据查询,它将在后台为我们生成适当的连接和 SQL 语句。

Using the Code

使用前,我们需要设置数据库配置。我们只需执行一次,例如,在应用程序的启动例程中。

DatabaseObjectShared 是一个包含连接数据库参数的类。DataFunctionsSQLite 是该框架支持的数据库之一。目前支持 OracleMySQLSQLite。PostgreSQL 和 SQLServer 即将支持。

var _file = Path.Combine(_dataBasePath, "RednetAccess.db3");
var _dbFuncName = "MyDataFunctionName";
var _function = new DataFunctionsSQLite() { DatabaseFile = _file, Name = _dbFuncName };

DatabaseObjectShared.DataFunctions.Clear();
DatabaseObjectShared.DataFunctions.Add(_dbFuncName, _function);
DatabaseObjectShared.DefaultDataFunctionName = _dbFuncName;

在这个框架中,我们有一个名为 DatabaseObject<> 的泛型类,它以静态和实例方式封装了所有用于数据处理(查询和 CRUD)的方法。我们将创建继承自它的类,以便我们可以操作数据。

定义我们的 User 类示例

public enum UserType
{
    Simple,
    Administrator
}

public class User : DatabaseObject<User>
{
    [FieldDef(IsPrimaryKey = true)]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }
    public UserType UserType { get; set; }
}

在这里,我们定义了一个继承自 DatabaseObject<> 类的 User 类,包含四个字段,并将 Id 属性设置为 FieldDefAttribute,其 IsPrimaryKey 字段设置为 true。即使您的表没有主键,您也必须定义一个属性作为主键。这对于在 CRUD 操作中定位记录非常重要。

继承自 DatabaseObject<> 后,我们的 User 类现在有了一些新方法。

使用实例 SaveChanges() 方法添加一些行

    var _user1 = new User { Id = 1, Name = 'Nelson', 
                            Password = 'xyz', UserType = UserType.Administrator };
    var _user2 = new User { Id = 2, Name = 'Robert', 
                            Password = 'xyz', UserType = UserType.Simple };
    var _user3 = new User { Id = 3, Name = 'Willian', Password = 'xyz', 
                            UserType = UserType.Simple };
    var _user4 = new User { Id = 4, Name = 'Thompson', 
                            Password = 'xyz', UserType = UserType.Simple };

    _user1.SaveChanges();
    _user2.SaveChanges();
    _user3.SaveChanges();
    _user4.SaveChanges();

SaveChanges() 方法会检查记录是否已存在于数据库表中。如果存在,则对其进行更新,否则插入。

在这里,我们将使用静态 Load() 方法检索数据

var _user = User.Load(u => u.Id = 1);

Console.WriteLine(_user.Name);

// output -> 'Nelson'

与 Load() 方法类似,Query() 方法可以检索 User 类列表。

var _users = User.Query(u => u.UserType = UserType.Simple);

foreach(var _user in _users)
{
    Console.WriteLine(_user.Name);
}
// output -> 'Robert'
// output -> 'Willian'
// output -> 'Thompson'

Load()Query() 方法都使用相同的机制检索数据。区别在于 Load() 在未找到数据时返回 null,而 Query() 返回一个空列表。

Rednet.DataAccess 在框架内部使用了 Dapper.Net 的早期版本之一,我们可以在相同的方法中使用这种方法加载带有自定义 SQL 语句的数据。

var _users = User.Query("select * from Users Where UserType = @UserType", 
                         new {UserType = UserType.Simple});

foreach(var _user in _users)
{
    Console.WriteLine(_user.Name);
}
// output -> 'Robert'
// output -> 'Willian'
// output -> 'Thompson'

FieldDefAttribute

FieldDefAttribute 可以改变您的类的行为。它是框架中非常重要的一部分,它指示 Rednet.DataAccess 引擎将如何处理您的类中的某些属性。下面是 FieldDefAttribute 的一些主要字段描述

属性名称 数据类型 描述
AutomaticValue 枚举 这有三个值
None = 默认 - 不做任何操作。
AutoIncrement = 指示具有此类型的属性由后端计算,并且不会包含在插入字段语句中。在 SQLite 数据库中,您可以使用它来生成自增列。
BackEndCalculated = 具有此类型的属性被视为 AutoIncrement 值,但不会自动生成值。就像 Oracle 中的对象 Sequences 一样,您需要做一些工作将新值放入您的列中(例如触发器),Rednet.DataAccess 将其取回您的对象。
IsPrimaryKey 布尔值 指示属性是主键约束的一部分。这可以应用于类中的多个属性。所有继承自 DatabaseObject<> 的类必须至少有一个属性用 FieldDefAttribute 装饰并设置为 IsPrimaryKey = true 才能正常运行。
IgnoreForSave 布尔值 标记此项的属性将被框架中的 DML 语句忽略。当您需要创建一些在运行时生成且表中不存在数据的属性时,这很有用。

填充内部对象

使用 Rednet.DataAccess,我们可以填充类中存在的内部对象。为此,我们需要在属性上使用 JoinFieldAttribute,指示框架在生成 SQL 语句时将包含 innerleftright join 命令,并用结果填充内部属性。请参见代码

    public class Purchase : DatabaseObject<Purchase>
    {
        [FieldDef(AutomaticValue = AutomaticValue.AutoIncrement, IsPrimaryKey = true)]
        public int PurchaseId { get; set; }

        public DateTime PurchaseDate { get; set; }

        [FieldDef(IgnoreForSave = true)]
        public double TotalPurchase
        {
            get { return this.OrderItems.Sum(i => i.TotalItem); }
        }

        [JoinField(SourceColumnNames = new [] { "OrderId" }, 
         TargetColumnNames = new [] {"OrderId"}, 
         JoinRelation = JoinRelation.OneToMany, JoinType = JoinType.LeftJoin)]
        public ObservableCollection<PurchaseItem> OrderItems { get; set; } 
    }

    public class PurchaseItem : DatabaseObject<PurchaseItem>
    {
        [FieldDef(IsPrimaryKey = true)]
        public int PurchaseId { get; set; }

        [FieldDef(IsPrimaryKey = true)]
        public int ItemId { get; set; }

        public int Amount { get; set; }

        public double Price { get; set; }

        [FieldDef(IgnoreForSave = true)]
        public double TotalItem
        {
            get
            {
                return this.Amount * this.Price;
            }
        }
    }   

放入一些数据进行测试

    for (var _id = 1; _id < 3; _id++)
    {
        var _order = new Purchase 
                     { PurchaseId = _id, PurchaseDate = DateTime.Parse("2016-05-25") };
        _order.SaveChanges(); 

        var _amount = 1;
        for (int _item = 1; _item < (_id == 1 ? 4 : 3); _item++)
        {
            var _orderitem = new PurchaseItem { PurchaseId = _id, 
                             ItemId = _item, Amount = _amount, Price = 1.0 };
            _orderitem.SaveChanges();
            _amount++;
        }
    }

一行代码,您的所有记录...

    var _orders = Purchase.Query(); // get all rows on table

并显示出来!

    foreach (var _order in _orders)
    {
        Console.WriteLine(string.Format("Purchase: {0} - PurchaseDate: {1} - Total: {2}", 
                          _order.PurchaseId, _order.PurchaseDate, _order.TotalPurchase));
        foreach (var _item in _order.OrderItems)
        {
            Console.WriteLine("Item: {0} - Amount: {1} - Price: {2} - Total: {3}", 
                               _item.ItemId, _item.Amount, _item.Price, _item.TotalItem);
        }
    }

    //-> output
    //Purchase: 1 - PurchaseDate: 5/25/2016 12:00:00 AM - Total: 6
    //Item: 1 - Amount: 1 - Price: 1 - Total: 1
    //Item: 2 - Amount: 2 - Price: 1 - Total: 2
    //Item: 3 - Amount: 3 - Price: 1 - Total: 3
    //Purchase: 2 - PurchaseDate: 5/25/2016 12:00:00 AM - Total: 3
    //Item: 1 - Amount: 1 - Price: 1 - Total: 1
    //Item: 2 - Amount: 2 - Price: 1 - Total: 2    

JoinFieldAttribute 的主要属性

属性名称 类型 描述
SourceColumnNames 字符串数组 一个包含当前表/类对象中源列名的数组,该源列名将与外键表/类连接。
TargetColumnNames 字符串数组 一个包含外键表/类对象的目标列名的数组,该目标列名将与源表/类连接。此列名列表必须与用 JoinFieldAttribute 属性修饰的类型相同。
JoinRelation 枚举 通知框架如何将此数据填充到属性中。
OneToOne = 通知该类型只有一个行将被填充到属性中。
OneToMany = 通知零个或多个行将被填充到属性中。通常,这用于 IList<>IObservables<>IEnumerables<> 等类型的属性。
JoinType 枚举 通知框架如何根据 Join 命令构造 SQL 语句。
InnerJoin = 只要两表中的列之间存在匹配,就选择两表中的所有行。
LeftJoin = 选择左表(主对象类)中的所有行,以及右表(主对象类上的属性)中匹配的行。当没有匹配时,结果在右侧为 NULL
RightJoin = 选择右表(主对象类上的属性)中的所有行,以及左表(主对象类)中匹配的行。当没有匹配时,结果在左侧为 NULL

下面是一些删除数据的示例代码

    // list all rows and get last User row
    var _users = User.Query(); //--> null predicates return all rows from table
    var _user = _users.LastOrDefault();

    if (_user != null)
        _user.Delete();

    // delete all admin Users
    User.DeleteAll(u => u.UserType == UserType.Administrator);

    // delete all simple Users
    User.DeleteAll(u => u.UserType == UserType.Simple);

    // delete all rows
    User.DeleteAll(); //--> for all rows predicate must be null

实用函数

下面列出了可用于此的一些实用函数

函数名 类型 描述
FromJson() static 将 JSON 数据转换为相应的对象
ToJson() 实例 将对象导出为 JSON 数据格式
Exists() static 检查 static 对象上指示的谓词是否存在于相应的数据库表中
Exists() 实例 检查实例化对象中的当前数据是否存在于相应的数据库表中
Clone() 实例 将当前对象克隆到新的实例对象中
CloneTo() 实例 使用当前对象的属性名称将当前对象克隆到新的实例泛型对象中以填充新对象。

一些代码示例

Json
    // load data from json string
    // UserType -> 0 = Simple / 1 = Administrator
    var _jsonData = 
        "{ Id : 9999, Name : \"Nelson Santos\", Password : \"123\", UserType : 1 }"; 
    var _user = User.FromJson(_jsonData);
    _user.SaveChanges();

    _user.ToJson();

    //--> out put 
    //{ 
    //    Id : 9999, 
    //    Name : "Nelson Santos", 
    //    Password : "123", 
    //    UserType : 1 
    //}

注释

  • ToJson() 可以与可选参数 compressString(默认为 false)一起使用,以指导方法在返回数据之前压缩数据。
  • FromJson() 可以与可选参数 decompressString(默认为 false)一起使用,以指导方法在反序列化数据并返回新对象之前解压缩 string

检查数据是否存在(static 方法)

    var _exists = User.Exists(u => u.Id == 1); // can return true or false

检查数据是否存在(实例方法)

    var _jsonData = 
        "{ Id : 9999, Name : \"Nelson Santos\", Password : \"123\", UserType : 1 }"; 
    var _user = User.FromJson(_jsonData);
    var _exists = _user.Exists(); // can return true or false

在这里,他使用用 FieldDefAttribute (Id) 装饰的属性,其中 IsPrimaryKey 字段设置为 true,以识别在内部 SQL 语句中要使用的列。

关注点

我已将此框架用于我的 Xamarin Android、iOS 和 ASP.NET 项目。
这很有用,并且让我在工作中获得了一些敏捷性。

历史

  • 1.0.3 - 2016 年 5 月 26 日 - 第一个版本
© . All rights reserved.