數據庫操作
插件通常擁有自己的(de)表結構,用于(yú)存儲插件相關的(de)數據,本節将說(shuō)明如何創建數據庫表,以(yǐ)及如何在(zài)代碼中對數據進行操作。
配置數據庫表結構
爲(wéi / wèi)了(le/liǎo)讓 XYCMS 系統自動創建我們所需要(yào / yāo)的(de)數據庫表,我們需要(yào / yāo)在(zài)插件的(de) package.json 文件中對數據庫表結構進行配置。
爲(wéi / wèi)了(le/liǎo)讓 XYCMS 系統自動創建我們所需要(yào / yāo)的(de)數據庫表,我們需要(yào / yāo)對插件 package.json 配置文件的(de) extensions -> tables 節點進行設置,在(zài)此,我們以(yǐ)XYCMS 内容相冊插件 (opens new window)的(de)package.json (opens new window)作爲(wéi / wèi)示例說(shuō)明表結構的(de)配置:
"tables": {
"xycms_photos": {
"columns": [
{
"attributeName": "SiteId",
"dataType": "Integer"
},
{
"attributeName": "ChannelId",
"dataType": "Integer"
},
{
"attributeName": "ContentId",
"dataType": "Integer"
},
{
"attributeName": "SmallUrl",
"dataType": "VarChar"
},
{
"attributeName": "MiddleUrl",
"dataType": "VarChar"
},
{
"attributeName": "LargeUrl",
"dataType": "VarChar"
},
{
"attributeName": "Taxis",
"dataType": "Integer"
},
{
"attributeName": "Description",
"dataType": "VarChar",
"dataLength": 2000
}
]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- xycms_photos:表名稱,可以(yǐ)任意取值,需要(yào / yāo)确保唯一(yī / yì /yí)性,插件啓動時(shí) XYCMS 系統将爲(wéi / wèi)插件創建對應的(de)數據庫表。
- columns:表字段集合。
- attributeName:字段名稱。
- dataType:字段類型。
- dataLength:字段長度,如果是(shì)字符串類型字段,不(bù)設置的(de)話默認長度爲(wéi / wèi)500。
系統支持的(de)字段類型有一(yī / yì /yí)下幾種:
屬性 | 類型 |
---|---|
Boolean | 布爾值 |
DateTime | 日期 |
Decimal | 小數 |
Integer | 整數 |
Text | 備注 |
VarChar | 字符串 |
由于(yú) XYCMS 系統支持多種數據庫,不(bù)同數據庫類型的(de)實際字段類型名稱與 dataType 的(de)名稱可能有區别。
如果插件版本更新的(de)同時(shí)更新了(le/liǎo)數據庫表結構,XYCMS 系統将負責把更新的(de)表結構同步到(dào)實際數據庫中。
需要(yào / yāo)注意的(de)是(shì),XYCMS 系統在(zài)創建和(hé / huò)同步表結構的(de)同時(shí),将默認創建以(yǐ)下字段:
屬性 | 類型 | 說(shuō)明 |
---|---|---|
Id | Integer | 自增長Id字段 |
Guid | VarChar | 字符串類型的(de)全局唯一(yī / yì /yí)标識符,XYCMS系統負責爲(wéi / wèi)此字段賦值并保持唯一(yī / yì /yí)性 |
ExtendValues | Text | 擴展字段 |
CreatedDate | DateTime | 數據創建時(shí)間,XYCMS系統負責爲(wéi / wèi)此字段賦值 |
LastModifiedDate | DateTime | 數據修改時(shí)間,XYCMS系統負責爲(wéi / wèi)此字段賦值 |
數據操作框架
XYCMS 系統将在(zài)插件第一(yī / yì /yí)次加載的(de)時(shí)候核對表字段并創建或者同步表結構到(dào)數據庫,我們接下來(lái)需要(yào / yāo)做的(de)就(jiù)是(shì)編寫代碼操作數據庫,對數據進行增删改查的(de)操作。
實現數據的(de)操作方式有很多,沒有必須遵守的(de)規則,XYCMS 系統使用的(de)數據操作類庫是(shì)我們自行開發的(de) Datory 框架,Datory 框架是(shì)我們在(zài)流行的(de) Dapper 框架基礎上(shàng)封裝了(le/liǎo)一(yī / yì /yí)些操作接口而(ér)成,如果熟悉 Dapper 框架會發現操作接口非常類似。
我們以(yǐ) XYCMS 自帶的(de) Datory 框架作爲(wéi / wèi)演示并不(bù)是(shì)推薦使用此框架,大(dà)家在(zài)實際開發過程中應該盡量使用自己熟悉的(de)數據庫操作類庫進行數據操作。
定義表實體類
我們首先定義一(yī / yì /yí)個(gè)實體類,作爲(wéi / wèi)操作數據的(de)實體模型,我們以(yǐ) XYCMS 内容相冊插件 (opens new window)的(de) Photo (opens new window)類作爲(wéi / wèi)示例:
using Datory;
using Datory.Annotations;
namespace XYCMS.Photos.Models
{
[DataTable("xycms_photos")]
public class Photo : Entity
{
[DataColumn]
public int SiteId { get; set; }
[DataColumn]
public int ChannelId { get; set; }
[DataColumn]
public int ContentId { get; set; }
[DataColumn]
public string SmallUrl { get; set; }
[DataColumn]
public string MiddleUrl { get; set; }
[DataColumn]
public string LargeUrl { get; set; }
[DataColumn]
public int Taxis { get; set; }
[DataColumn(Length = 2000)]
public string Description { get; set; }
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
可以(yǐ)看到(dào),Photo 實體類繼承了(le/liǎo) Datory 框架的(de) Entity 類,然後我們需要(yào / yāo)使用 [DataTable] 标識表名,同時(shí)使用 [DataColumn] 标識出(chū)字段。
繼承 Entity 類之(zhī)後,實體類将自動擁有Id(自增長Id字段)、Guid(全局唯一(yī / yì /yí)标識符)、ExtendValues(擴展字段)、CreatedDate(數據創建時(shí)間)、LastModifiedDate(數據修改時(shí)間)這(zhè)五個(gè)字段,并且這(zhè)五個(gè)字段的(de)值是(shì)由系統進行維護的(de)。
數據操作接口
定義好實體類後,我們需要(yào / yāo)定義數據操作接口,此接口定義了(le/liǎo)我們所需要(yào / yāo)用到(dào)的(de)增删改查等操作,我們以(yǐ) XYCMS 内容相冊插件 (opens new window)的(de) IPhotoRepository (opens new window)作爲(wéi / wèi)示例:
using System.Collections.Generic;
using System.Threading.Tasks;
using XYCMS.Photos.Models;
namespace XYCMS.Photos.Abstractions
{
public interface IPhotoRepository
{
Task<int> InsertAsync(Photo photoInfo);
Task UpdateAsync(Photo photo);
Task UpdateDescriptionAsync(int photoId, string description);
Task UpdateTaxisAsync(List<int> photoIds);
Task DeleteAsync(int photoId);
Task DeleteAsync(int siteId, int channelId, int contentId);
Task<Photo> GetFirstPhotoAsync(int siteId, int channelId, int contentId);
Task<int> GetCountAsync(int siteId, int channelId, int contentId);
Task<List<int>> GetPhotoContentIdListAsync(int siteId, int channelId, int contentId);
Task<List<Photo>> GetPhotosAsync(int siteId, int channelId, int contentId);
Task<Photo> GetAsync(int photoId);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
數據操作實現
定義好接口之(zhī)後,我們需要(yào / yāo)編寫此接口的(de)具體實現,我們以(yǐ) XYCMS 内容相冊插件 (opens new window)的(de) PhotoRepository (opens new window)作爲(wéi / wèi)示例:
using System.Collections.Generic;
using System.Threading.Tasks;
using Datory;
using XYCMS.Photos.Abstractions;
using XYCMS.Photos.Models;
using XYCMS.Services;
namespace XYCMS.Photos.Core
{
public class PhotoRepository : IPhotoRepository
{
private readonly Repository<Photo> _repository;
public PhotoRepository(ISettingsManager settingsManager)
{
_repository = new Repository<Photo>(settingsManager.Database);
}
public async Task<int> InsertAsync(Photo photoInfo)
{
var maxTaxis = await GetMaxTaxisAsync(photoInfo.SiteId, photoInfo.ChannelId, photoInfo.ContentId);
photoInfo.Taxis = maxTaxis + 1;
photoInfo.Id = await _repository.InsertAsync(photoInfo);
return photoInfo.Id;
}
public async Task UpdateAsync(Photo photo)
{
await _repository.UpdateAsync(photo);
}
public async Task UpdateDescriptionAsync(int photoId, string description)
{
await _repository.UpdateAsync(Q
.Set(nameof(Photo.Description), description)
.Where(nameof(Photo.Id), photoId)
);
}
public async Task UpdateTaxisAsync(List<int> photoIds)
{
var taxis = 1;
foreach (var photoId in photoIds)
{
await SetTaxisAsync(photoId, taxis);
taxis++;
}
}
public async Task DeleteAsync(int photoId)
{
await _repository.DeleteAsync(photoId);
}
public async Task DeleteAsync(int siteId, int channelId, int contentId)
{
await _repository.DeleteAsync(Q
.Where(nameof(Photo.SiteId), siteId)
.Where(nameof(Photo.ChannelId), channelId)
.Where(nameof(Photo.ContentId), contentId)
);
}
public async Task<Photo> GetFirstPhotoAsync(int siteId, int channelId, int contentId)
{
return await _repository.GetAsync(Q
.Where(nameof(Photo.SiteId), siteId)
.Where(nameof(Photo.ChannelId), channelId)
.Where(nameof(Photo.ContentId), contentId)
.OrderBy(nameof(Photo.Taxis))
);
}
public async Task<int> GetCountAsync(int siteId, int channelId, int contentId)
{
return await _repository.CountAsync(Q
.Where(nameof(Photo.SiteId), siteId)
.Where(nameof(Photo.ChannelId), channelId)
.Where(nameof(Photo.ContentId), contentId)
);
}
public async Task<List<int>> GetPhotoContentIdListAsync(int siteId, int channelId, int contentId)
{
return await _repository.GetAllAsync<int>(Q
.Select(nameof(Photo.Id))
.Where(nameof(Photo.SiteId), siteId)
.Where(nameof(Photo.ChannelId), channelId)
.Where(nameof(Photo.ContentId), contentId)
.OrderBy(nameof(Photo.Taxis))
);
}
public async Task<List<Photo>> GetPhotosAsync(int siteId, int channelId, int contentId)
{
return await _repository.GetAllAsync(Q
.Where(nameof(Photo.SiteId), siteId)
.Where(nameof(Photo.ChannelId), channelId)
.Where(nameof(Photo.ContentId), contentId)
.OrderBy(nameof(Photo.Taxis))
);
}
public async Task<Photo> GetAsync(int photoId)
{
return await _repository.GetAsync(photoId);
}
private async Task SetTaxisAsync(int id, int taxis)
{
await _repository.UpdateAsync(Q
.Set(nameof(Photo.Taxis), taxis)
.Where(nameof(Photo.Id), id)
);
}
private async Task<int> GetMaxTaxisAsync(int siteId, int channelId, int contentId)
{
return await _repository.MaxAsync(nameof(Photo.Taxis), Q
.Where(nameof(Photo.SiteId), siteId)
.Where(nameof(Photo.ChannelId), channelId)
.Where(nameof(Photo.ContentId), contentId)
) ?? 0;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
可以(yǐ)看到(dào),PhotoRepository 類繼承了(le/liǎo) IPhotoRepository 接口,同時(shí)在(zài)構造函數中注入了(le/liǎo) XYCMS 系統的(de) ISettingsManager 接口,ISettingsManager 接口中将包含數據庫連接信息。
由于(yú)我們示例使用的(de)是(shì) Datory 框架,所以(yǐ)我們通過 Datory 框架的(de) Repository<Photo> 泛型類封裝了(le/liǎo) Photo 實體類的(de)所有操作,我們在(zài) PhotoRepository 類中所要(yào / yāo)做的(de)隻是(shì)調用數據庫操作接口。
注入數據操作接口
最後,我們需要(yào / yāo)将 IPhotoRepository 接口注入到(dào)系統中,使 IPhotoRepository 在(zài)需要(yào / yāo)的(de)時(shí)候可以(yǐ)直接使用。
我們以(yǐ) XYCMS 内容相冊插件 (opens new window)的(de) Startup (opens new window)類作爲(wéi / wèi)示例:
using Microsoft.Extensions.DependencyInjection;
using XYCMS.Photos.Abstractions;
using XYCMS.Photos.Core;
using XYCMS.Plugins;
namespace XYCMS.Photos
{
public class Startup : IPluginConfigureServices
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IPhotoRepository, PhotoRepository>();
services.AddScoped<IPhotoManager, PhotoManager>();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以(yǐ)看到(dào)在(zài)插件初始化方法 ConfigureServices 的(de)第一(yī / yì /yí)行注入了(le/liǎo) IPhotoRepository 接口。
使用數據操作接口
現在(zài),我們可以(yǐ)直接使用數據操作接口對數據進行操作了(le/liǎo)。
我們以(yǐ) XYCMS 内容相冊插件 (opens new window)的(de) PhotosController (opens new window)類作爲(wéi / wèi)示例:
public partial class PhotosController : ControllerBase
{
private const string Route = "photos/photos";
private const string RouteUpload = "photos/photos/actions/upload";
private readonly IAuthManager _authManager;
private readonly IPathManager _pathManager;
private readonly ISiteRepository _siteRepository;
private readonly IPhotoManager _photoManager;
private readonly IPhotoRepository _photoRepository;
public PhotosController(IAuthManager authManager, IPathManager pathManager, ISiteRepository siteRepository, IPhotoManager photoManager, IPhotoRepository photoRepository)
{
_authManager = authManager;
_pathManager = pathManager;
_siteRepository = siteRepository;
_photoManager = photoManager;
_photoRepository = photoRepository;
}
......
[HttpGet, Route(Route)]
public async Task<ActionResult<GetResult>> Get([FromQuery] ContentRequest request)
{
if (!await _authManager.HasContentPermissionsAsync(request.SiteId, request.ChannelId, PhotoManager.PermissionsContent))
return Unauthorized();
var site = await _siteRepository.GetAsync(request.SiteId);
var photos = await _photoRepository.GetPhotosAsync(request.SiteId, request.ChannelId, request.ContentId);
foreach (var photo in photos)
{
photo.LargeUrl = await _pathManager.ParseSiteUrlAsync(site, photo.LargeUrl, true);
photo.MiddleUrl = await _pathManager.ParseSiteUrlAsync(site, photo.MiddleUrl, true);
photo.SmallUrl = await _pathManager.ParseSiteUrlAsync(site, photo.SmallUrl, true);
}
return new GetResult
{
Photos = photos
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
可以(yǐ)看到(dào),我們在(zài) Get 方法中調用了(le/liǎo) IPhotoRepository 的(de) GetPhotosAsync 方法,以(yǐ)獲取圖片實體列表。