Rednet DataAccess






4.38/5 (8投票s)
Rednet.DataAccess 是另一个用于数据处理的组件,它简化了您的使用。
- 下载源代码 - 338.8 KB
- 获取最新源代码,请访问 GitHub:https://github.com/NelsonSantos/Rednet.DataAccess
- 从 NuGet 安装软件包:https://nuget.net.cn/packages/Rednet.DataAccess/
引言
在我漫长的数据处理之旅中,我使用了很多数据访问库来简化开发,但有时这些库总是出现一些难以解决的问题,在大多数情况下,我总是需要编写 SQL 查询来实现数据检索和性能。
编写 SQL 查询的过程总是迫使我记住项目中不断变化的列名和表名。
背景
使用 Rednet.DataAccess
,我们将定义类,其中一些属性带有指示我们对表和列偏好的特性,并以强类型方式执行 CRUD 操作和数据查询,它将在后台为我们生成适当的连接和 SQL 语句。
Using the Code
使用前,我们需要设置数据库配置。我们只需执行一次,例如,在应用程序的启动例程中。
DatabaseObjectShared
是一个包含连接数据库参数的类。DataFunctionsSQLite
是该框架支持的数据库之一。目前支持 Oracle、MySQL 和 SQLite。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 语句时将包含 inner
、left
或 right 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 日 - 第一个版本