@
- Sqlite配置
- 建立實體
- 筆記實體類
- 筆記分組實體
- 筆記片段實體
- 筆記片段負載實體
- 筆記片段倉庫實體
- 筆記模板(場景)實體
- 筆記片段模板實體
- 筆記片段模板負載實體
- 配置EF
- 建立對映
- 遷移和種子資料
- 專案地址
Sqlite配置
應用程式裡使用Sqlite作為資料庫,使用EntityFramworkCore作為ORM,使用CodeFirst方式用EFCore初始化Sqlite資料庫檔案:mato.db
在MatoProductivity.Core專案的appsettings.json
中新增本地sqlite連線字串
"ConnectionStrings": {
"Default": "Data Source=file:{0};"
},
...
這裡檔案是一個佔位符,透過程式碼hardcode到配置檔案
在MatoProductivityCoreModule.cs中,重寫PreInitialize並設定Configuration.DefaultNameOrConnectionString:
public override void PreInitialize()
{
LocalizationConfigurer.Configure(Configuration.Localization);
Configuration.Settings.Providers.Add<CommonSettingProvider>();
string documentsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), MatoProductivityConsts.LocalizationSourceName);
var configuration = AppConfigurations.Get(documentsPath, development);
var connectionString = configuration.GetConnectionString(MatoProductivityConsts.ConnectionStringName);
var dbName = "mato.db";
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), MatoProductivityConsts.LocalizationSourceName, dbName);
Configuration.DefaultNameOrConnectionString = String.Format(connectionString, dbPath);
base.PreInitialize();
}
建立實體
接下來定義實體類
筆記實體類
筆記用於儲存實體,在筆記列表中,每個筆記都有標題和內容,建立時間等內容。
定義於\MatoProductivity.Core\Models\Entities\Note.cs
public class Note : FullAuditedEntity<long>
{
public Note()
{
}
public Note(string name, bool isHidden, bool isRemovable)
{
Title = name;
IsHidden = isHidden;
IsRemovable = isRemovable;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public ICollection<NoteSegment> NoteSegments { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public string Status { get; set; }
public string Desc { get; set; }
public string Icon { get; set; }
public string Color { get; set; }
public string BackgroundColor { get; set; }
public string BackgroundImage { get; set; }
public string PreViewContent { get; set; }
public bool IsEditable { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
public bool CanSimplified { get; set; }
}
筆記分組實體
定義於\MatoProductivity.Core\Models\Entities\NoteGroup.cs
public class NoteGroup : FullAuditedEntity<long>
{
public NoteGroup()
{
}
public NoteGroup(string name, bool isHidden, bool isRemovable)
{
Title = name;
IsHidden = isHidden;
IsRemovable = isRemovable;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public string Title { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
public ICollection<Note> Notes { get; set; }
}
筆記片段實體
定義於\MatoProductivity.Core\Models\Entities\NoteSegment.cs
public class NoteSegment : FullAuditedEntity<long>, INoteSegment
{
public NoteSegment()
{
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
[ForeignKey(nameof(NoteId))]
public Note Note { get; set; }
public ICollection<NoteSegmentPayload> NoteSegmentPayloads { get; set; }
public long NoteId { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public string Status { get; set; }
public string Desc { get; set; }
public string Icon { get; set; }
public string Color { get; set; }
public int Rank { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
public INoteSegmentPayload GetNoteSegmentPayload(string key)
{
if (NoteSegmentPayloads != null)
{
return NoteSegmentPayloads.FirstOrDefault(c => c.Key == key);
}
return default;
}
public void SetNoteSegmentPayload(INoteSegmentPayload noteSegmentPayload)
{
if (NoteSegmentPayloads != null)
{
var currentPayload = NoteSegmentPayloads.FirstOrDefault(c => c.Key == noteSegmentPayload.Key);
if (currentPayload != null)
{
NoteSegmentPayloads.Remove(currentPayload);
}
if (!this.IsTransient())
{
(noteSegmentPayload as NoteSegmentPayload).NoteSegmentId = this.Id;
}
NoteSegmentPayloads.Add((noteSegmentPayload as NoteSegmentPayload));
}
}
public INoteSegmentPayload GetOrSetNoteSegmentPayload(string key, INoteSegmentPayload noteSegmentPayload)
{
if (NoteSegmentPayloads != null)
{
var currentPayload = NoteSegmentPayloads.FirstOrDefault(c => c.Key == key);
if (currentPayload != null)
{
return currentPayload;
}
if (noteSegmentPayload != null)
{
if (!this.IsTransient())
{
(noteSegmentPayload as NoteSegmentPayload).NoteSegmentId = this.Id;
}
NoteSegmentPayloads.Add((noteSegmentPayload as NoteSegmentPayload));
}
return noteSegmentPayload;
}
return noteSegmentPayload;
}
}
筆記片段負載實體
筆記片段負載與筆記片段實體為一對多的關係,用於儲存筆記片段的詳細內容。
定義於\MatoProductivity.Core\Models\Entities\NoteSegmentPayload.cs
public class NoteSegmentPayload : FullAuditedEntity<long>, INoteSegmentPayload
{
public NoteSegmentPayload()
{
}
public NoteSegmentPayload(string key, object value, string valuetype = null)
{
if (value is string)
{
this.SetStringValue((value as string).ToString());
}
else if (value is byte[])
{
this.Value = value as byte[];
}
else if (value is DateTime)
{
this.SetStringValue(((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"));
}
else
{
this.SetStringValue(value.ToString());
}
this.Key = key;
this.ValueType = valuetype;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
[ForeignKey(nameof(NoteSegmentId))]
public NoteSegment NoteSegment { get; set; }
public long NoteSegmentId { get; set; }
public string Key { get; set; }
public byte[] Value { get; set; }
public string ValueType { get; set; }
[NotMapped]
public string StringValue => GetStringValue();
public T GetConcreteValue<T>() where T : struct
{
var value = Encoding.UTF8.GetString(Value);
T result = value.To<T>();
return result;
}
public string GetStringValue()
{
var value = Encoding.UTF8.GetString(Value);
return value;
}
public void SetStringValue(string value)
{
this.Value = Encoding.UTF8.GetBytes(value);
}
}
筆記片段倉庫實體
用於在編輯筆記頁面的新增片段選單中,載入所有可用的片段
定義於\MatoProductivity.Core\Models\Entities\NoteSegmentStore.cs
public class NoteSegmentStore : Entity<long>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public string Category { get; set; }
public string Status { get; set; }
public string Desc { get; set; }
public string Icon { get; set; }
public string Color { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
}
筆記模板(場景)實體
定義於\MatoProductivity.Core\Models\Entities\NoteTemplate.cs
public class NoteTemplate : FullAuditedEntity<long>
{
public NoteTemplate()
{
}
public NoteTemplate(string name, bool isHidden, bool isRemovable)
{
Title = name;
IsHidden = isHidden;
IsRemovable = isRemovable;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public ICollection<NoteSegmentTemplate> NoteSegmentTemplates { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public string Status { get; set; }
public string Desc { get; set; }
public string Icon { get; set; }
public string Color { get; set; }
public string BackgroundColor { get; set; }
public string BackgroundImage { get; set; }
public string PreViewContent { get; set; }
public bool IsEditable { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
public bool CanSimplified { get; set; }
}
筆記片段模板實體
定義於\MatoProductivity.Core\Models\Entities\NoteSegmentTemplate.cs
public class NoteSegmentTemplate : FullAuditedEntity<long>, INoteSegment
{
public NoteSegmentTemplate()
{
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
[ForeignKey(nameof(NoteTemplateId))]
public NoteTemplate NoteTemplate { get; set; }
public ICollection<NoteSegmentTemplatePayload> NoteSegmentTemplatePayloads { get; set; }
public long NoteTemplateId { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public string Status { get; set; }
public string Desc { get; set; }
public string Icon { get; set; }
public string Color { get; set; }
public int Rank { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
public INoteSegmentPayload GetNoteSegmentPayload(string key)
{
if (NoteSegmentTemplatePayloads != null)
{
return NoteSegmentTemplatePayloads.FirstOrDefault(c => c.Key == key);
}
return default;
}
public void SetNoteSegmentPayload(INoteSegmentPayload noteSegmentPayload)
{
if (NoteSegmentTemplatePayloads != null)
{
var currentPayload = NoteSegmentTemplatePayloads.FirstOrDefault(c => c.Key == noteSegmentPayload.Key);
if (currentPayload != null)
{
NoteSegmentTemplatePayloads.Remove(currentPayload);
}
if (!this.IsTransient())
{
(noteSegmentPayload as NoteSegmentTemplatePayload).NoteSegmentTemplateId = this.Id;
}
NoteSegmentTemplatePayloads.Add((noteSegmentPayload as NoteSegmentTemplatePayload));
}
}
public INoteSegmentPayload GetOrSetNoteSegmentPayload(string key, INoteSegmentPayload noteSegmentPayload)
{
if (NoteSegmentTemplatePayloads != null)
{
var currentPayload = NoteSegmentTemplatePayloads.FirstOrDefault(c => c.Key == key);
if (currentPayload != null)
{
return currentPayload;
}
if (noteSegmentPayload != null)
{
if (!this.IsTransient())
{
(noteSegmentPayload as NoteSegmentTemplatePayload).NoteSegmentTemplateId = this.Id;
}
NoteSegmentTemplatePayloads.Add((noteSegmentPayload as NoteSegmentTemplatePayload));
}
return noteSegmentPayload;
}
return noteSegmentPayload;
}
}
筆記片段模板負載實體
定義於\MatoProductivity.Core\Models\Entities\NoteSegmentTemplatePayload.cs
public class NoteSegmentTemplatePayload : FullAuditedEntity<long>, INoteSegmentPayload
{
public NoteSegmentTemplatePayload()
{
}
public NoteSegmentTemplatePayload(string key, object value, string valuetype = null)
{
if (value is string)
{
this.SetStringValue((value as string).ToString());
}
else if (value is byte[])
{
this.Value = value as byte[];
}
else if (value is DateTime)
{
this.SetStringValue(((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"));
}
else
{
this.SetStringValue(value.ToString());
}
this.Key = key;
this.ValueType = valuetype;
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
[ForeignKey(nameof(NoteSegmentTemplateId))]
public NoteSegmentTemplate NoteSegmentTemplate { get; set; }
public long NoteSegmentTemplateId { get; set; }
public string Key { get; set; }
public byte[] Value { get; set; }
public string ValueType { get; set; }
[NotMapped]
public string StringValue => GetStringValue();
public T GetConcreteValue<T>() where T : struct
{
var value = Encoding.UTF8.GetString(Value);
T result = value.To<T>();
return result;
}
public string GetStringValue()
{
var value = Encoding.UTF8.GetString(Value);
return value;
}
public void SetStringValue(string value)
{
this.Value = Encoding.UTF8.GetBytes(value);
}
}
配置EF
資料庫上下文物件MatoProductivityDbContext
定義如下
public class MatoProductivityDbContext : AbpDbContext
{
//Add DbSet properties for your entities...
public DbSet<Note> Note { get; set; }
public DbSet<NoteGroup> NoteGroup { get; set; }
public DbSet<NoteSegment> NoteSegment { get; set; }
public DbSet<NoteSegmentStore> NoteSegmentStore { get; set; }
public DbSet<NoteSegmentPayload> NoteSegmentPayload { get; set; }
public DbSet<NoteTemplate> NoteTemplate { get; set; }
public DbSet<NoteSegmentTemplate> NoteSegmentTemplate { get; set; }
public DbSet<NoteSegmentTemplatePayload> NoteSegmentTemplatePayload { get; set; }
public DbSet<Theme> Theme { get; set; }
public DbSet<Setting> Setting { get; set; }
public MatoProductivityDbContext(DbContextOptions<MatoProductivityDbContext> options)
: base(options)
{
}
}
MatoProductivity.EntityFrameworkCore是應用程式資料庫的維護和管理專案,依賴於Abp.EntityFrameworkCore。
在MatoProductivity.EntityFrameworkCore專案中csproj檔案中,引用下列包
<PackageReference Include="Abp.EntityFrameworkCore" Version="7.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
在該專案MatoProductivityEntityFrameworkCoreModule.cs 中,將註冊上下文物件,並在程式初始化執行遷移,此時將在裝置上生成mato.db
檔案
public override void PostInitialize()
{
Helper.WithDbContextHelper.WithDbContext<MatoProductivityDbContext>(IocManager, RunMigrate);
if (!SkipDbSeed)
{
SeedHelper.SeedHostDb(IocManager);
}
}
public static void RunMigrate(MatoProductivityDbContext dbContext)
{
dbContext.Database.Migrate();
}
建立對映
從場景到筆記,或者說從模板到例項,我們需要對映,例如從筆記片段選單中選擇一個片段新增,那麼需要從筆記片段倉庫實體(NoteSegmentStore)對映到筆記片段實體(NoteSegment)或者,在編輯場景中,對映到筆記片段模板實體(NoteSegmentTemplate)。
[AutoMapTo(typeof(NoteSegment), typeof(NoteSegmentTemplate))]
public class NoteSegmentStore : Entity<long>
{
...
}
使用時:
var note = ObjectMapper.Map<NoteSegment>(noteSegmentStore);
ABP框架預設使用AutoMapper
進行對映,所以需要配置對映關係。
Configuration.Modules.AbpAutoMapper().Configurators.Add(config =>
{
IgnoreAbpProperties(config.CreateMap<NoteTemplate, Note>()
.ForMember(
c => c.NoteSegments,
options => options.MapFrom(input => input.NoteSegmentTemplates))
.ForMember(
c => c.Id,
options => options.Ignore()));
IgnoreAbpProperties(config.CreateMap<Note, NoteTemplate>()
.ForMember(
c => c.NoteSegmentTemplates,
options => options.MapFrom(input => input.NoteSegments))
.ForMember(
c => c.Id,
options => options.Ignore()));
IgnoreAbpProperties(config.CreateMap<NoteSegmentTemplate, NoteSegment>()
.ForMember(
c => c.Note,
options => options.MapFrom(input => input.NoteTemplate))
.ForMember(
c => c.NoteSegmentPayloads,
options => options.MapFrom(input => input.NoteSegmentTemplatePayloads))
.ForMember(
c => c.NoteId,
options => options.Ignore())
.ForMember(
c => c.Id,
options => options.Ignore()));
IgnoreAbpProperties(config.CreateMap<NoteSegmentStore, NoteSegment>()
.ForMember(
c => c.Id,
options => options.Ignore()));
IgnoreAbpProperties(config.CreateMap<NoteSegment, NoteSegmentTemplate>()
.ForMember(
c => c.NoteTemplate,
options => options.MapFrom(input => input.Note))
.ForMember(
c => c.NoteTemplateId,
options => options.Ignore())
.ForMember(
c => c.NoteSegmentTemplatePayloads,
options => options.MapFrom(input => input.NoteSegmentPayloads))
.ForMember(
c => c.Id,
options => options.Ignore()));
IgnoreAbpProperties(config.CreateMap<NoteSegmentTemplatePayload, NoteSegmentPayload>()
.ForMember(
c => c.NoteSegment,
options => options.MapFrom(input => input.NoteSegmentTemplate))
.ForMember(
c => c.NoteSegmentId,
options => options.Ignore())
.ForMember(
c => c.Id,
options => options.Ignore()));
IgnoreAbpProperties(
config.CreateMap<NoteSegmentPayload, NoteSegmentTemplatePayload>()
.ForMember(
c => c.NoteSegmentTemplate,
options => options.MapFrom(input => input.NoteSegment))
.ForMember(
c => c.NoteSegmentTemplateId,
options => options.Ignore()));
});
遷移和種子資料
MatoProductivity.EntityFrameworkCore.Seed.SeedHelper
可在程式啟動時,訪問資料庫,並初始化種子資料。
public override void PostInitialize()
{
Helper.WithDbContextHelper.WithDbContext<MatoProductivityDbContext>(IocManager, RunMigrate);
if (!SkipDbSeed)
{
SeedHelper.SeedHostDb(IocManager);
}
}
它透過SkipDbSeed
來決定是否跳過執行種子資料初始化。我們需要在安裝完成App後第一次執行才執行種子資料初始化。
MAUI中提供了VersionTracking.Default.IsFirstLaunchEver
方式獲取是否是第一次在此裝置上啟動應用,請檢視官方文件
public override async void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(MatoProductivityModule).GetAssembly());
if (VersionTracking.Default.IsFirstLaunchEver)
{
MatoProductivityEntityFrameworkCoreModule.SkipDbSeed = false;
}
else
{
MatoProductivityEntityFrameworkCoreModule.SkipDbSeed = true;
}
}
在InitialDbBuilder中我們定義了大多數的業務初始資料,具體的實現方式請查閱原始碼。
internal void Create()
{
CreateSetting("Theme", "Light");
CreateSetting("DetailPageMode", "PreviewPage");
CreateNoteSegmentStore("時間戳", "時間/提醒", "DateTimeSegment", "記錄一個瞬時時間", FaIcons.IconClockO, "#D8292B");
CreateNoteSegmentStore("計時器", "時間/提醒", "TimerSegment", "建立一個計時器,當它結束時會通知您", FaIcons.IconBell, "#D8292B");
CreateNoteSegmentStore("筆記", "文字", "TextSegment", "隨時用文字記錄您的想法", FaIcons.IconStickyNoteO, "#E1A08B");
CreateNoteSegmentStore("Todo", "文字", "TodoSegment", "記錄一個Todo專案", FaIcons.IconCheckSquareO, "#E1A08B");
CreateNoteSegmentStore("數值", "文字", "KeyValueSegment", "記錄數值,以便統計資料", FaIcons.IconLineChart, "#E1A08B");
CreateNoteSegmentStore("手繪", "檔案", "ScriptSegment", "建立一個手繪", FaIcons.IconPaintBrush, "#AD9CC2");
CreateNoteSegmentStore("照片/影片", "檔案", "MediaSegment", "拍照或攝像", FaIcons.IconCamera, "#AD9CC2");
CreateNoteSegmentStore("文件", "檔案", "DocumentSegment", "從您裝置中選取一個文件", FaIcons.IconFile, "#AD9CC2");
CreateNoteSegmentStore("錄音", "檔案", "VoiceSegment", "記錄一段聲音", FaIcons.IconMicrophone, "#AD9CC2");
CreateNoteSegmentStore("地點", "其它", "LocationSegment", "獲取當前地點,或者從地圖上選取一個地點", FaIcons.IconMapMarker, "#6D987C");
CreateNoteSegmentStore("天氣", "其它", "WeatherSegment", "獲取當前天氣資訊", FaIcons.IconCloud, "#6D987C");
CreateNoteSegmentStore("聯絡人", "其它", "ContactSegment", "從您裝置的通訊錄中選擇一個聯絡人", FaIcons.IconUser, "#6D987C");
}
專案地址
GitHub:MatoProductivity