Skip to content

Commit

Permalink
谢谢微软喵!谢谢微软喵!谢谢微软喵!谢谢微软喵!谢谢微软喵!谢谢微软喵!
Browse files Browse the repository at this point in the history
  • Loading branch information
DearVa committed Sep 6, 2022
1 parent 135da52 commit 98425f3
Show file tree
Hide file tree
Showing 27 changed files with 683 additions and 650 deletions.
6 changes: 3 additions & 3 deletions ExplorerEx/Command/FileItemCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ public class FileItemCommand : ICommand {
break;
}
case "Delete": { // 删除一个或多个文件,按住shift就是强制删除
if (Folder.IsReadonly) {
break;
}
var items = Items;
if (items.Count == 0) {
break;
}
if (Folder.IsReadonly) {
break;
}
if ((Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift) { // 没有按Shift
if (!Settings.Current[Settings.CommonSettings.DontAskWhenRecycle].GetBoolean()) {
if (ContentDialog.Ask("#AreYouSureToRecycleTheseFiles".L()) == ContentDialog.ContentDialogResult.Cancel) {
Expand Down
2 changes: 1 addition & 1 deletion ExplorerEx/Database/DbMain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace ExplorerEx.Database;

internal class DbMain {
internal static class DbMain {
public static IBookmarkDbContext BookmarkDbContext { get; }

public static IFileViewDbContext FileViewDbContext { get; }
Expand Down
10 changes: 9 additions & 1 deletion ExplorerEx/Database/Interface/IBookmarkDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ public interface IBookmarkDbContext : IDatabase {

BookmarkCategory? FirstOrDefault(Expression<Func<BookmarkCategory, bool>> match);
void Add(BookmarkCategory category);

/// <summary>
/// 注意,<bold>不会</bold>级联删除子项
/// </summary>
/// <param name="category"></param>
void Remove(BookmarkCategory category);
ObservableCollection<BookmarkCategory> AsObservableCollection();

BookmarkCategory QueryBookmarkCategory(string foreignKey);

BookmarkItem[] QueryBookmarkItems(string foreignKey);
}
14 changes: 0 additions & 14 deletions ExplorerEx/Database/Shared/DbColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,4 @@ public class DbColumn : Attribute {
/// 存储时的最大长度
/// </summary>
public int MaxLength { get; set; } = -1;

/// <summary>
/// 说明是一个映射,和IsPrimaryKey相冲突,只能应用在<see cref="DbProperty&lt;"/>上
/// </summary>
public string? NavigateTo { get; set; }

public DbNavigateType NavigateType { get; set; }

public DbColumn() { }

public DbColumn(string navigateTo, DbNavigateType navigateType) {
NavigateTo = navigateTo;
NavigateType = navigateType;
}
}
68 changes: 44 additions & 24 deletions ExplorerEx/Database/SqlSugar/BookmarkSugarContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Windows;
Expand All @@ -13,42 +14,42 @@ public class BookmarkSugarContext : IBookmarkDbContext {
private readonly CachedSugarContext<BookmarkItem> bookmarkCtx;
private readonly CachedSugarContext<BookmarkCategory> categoryCtx;

private bool isLoaded;

public BookmarkSugarContext() {
categoryCtx = new CachedSugarContext<BookmarkCategory>("BookMarks.db");
bookmarkCtx = new CachedSugarContext<BookmarkItem>("BookMarks.db");
//categorySugarCache = new SugarCache<BookmarkCategory, BookmarkItem>(ConnectionClient,
// new SugarStrategy<BookmarkCategory, BookmarkItem>(itemSugarCache, (category, item) => {
// if (item.CategoryForeignKey == category.Name) {
// item.Category = category;
// category.Children?.Add(item);
// }
// }));
}

public async Task LoadAsync() {
await categoryCtx.LoadAsync();
await bookmarkCtx.LoadAsync();
await Task.Run(() => {
try {
if (categoryCtx.Count() == 0) {
var defaultCategory = new BookmarkCategory("DefaultBookmark".L());
categoryCtx.Add(defaultCategory);
categoryCtx.Save();
bookmarkCtx.Add(new BookmarkItem(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Documents".L(), defaultCategory));
bookmarkCtx.Add(new BookmarkItem(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Desktop".L(), defaultCategory));
bookmarkCtx.Save();
if (!isLoaded) {
await categoryCtx.LoadAsync();
await bookmarkCtx.LoadAsync();
await Task.Run(() => {
try {
if (categoryCtx.Count() == 0) {
var defaultCategory = new BookmarkCategory("DefaultBookmark".L());
categoryCtx.Add(defaultCategory);
categoryCtx.Save();
bookmarkCtx.Add(new BookmarkItem(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Documents".L(), defaultCategory));
bookmarkCtx.Add(new BookmarkItem(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Desktop".L(), defaultCategory));
bookmarkCtx.Save();
}
} catch (Exception e) {
MessageBox.Show("无法加载数据库,可能是权限不够或者数据库版本过旧,请删除Data文件夹后再试一次。\n错误为:" + e.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
Logger.Exception(e, false);
}
} catch (Exception e) {
MessageBox.Show("无法加载数据库,可能是权限不够或者数据库版本过旧,请删除Data文件夹后再试一次。\n错误为:" + e.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
Logger.Exception(e, false);
}
});
});
isLoaded = true;
}
}

public void Add(BookmarkItem bookmark) => bookmarkCtx.Add(bookmark);

public void Add(BookmarkCategory category) => categoryCtx.Add(category);

public void Remove(BookmarkCategory category) => categoryCtx.Remove(category);

public bool Contains(BookmarkItem bookmark) => bookmarkCtx.Contains(bookmark);

public bool Any(Expression<Func<BookmarkItem, bool>> match) => bookmarkCtx.Any(match);
Expand All @@ -57,7 +58,11 @@ public class BookmarkSugarContext : IBookmarkDbContext {

public BookmarkItem? FirstOrDefault(Expression<Func<BookmarkItem, bool>> match) => bookmarkCtx.FirstOrDefault(match);

public ObservableCollection<BookmarkCategory> AsObservableCollection() => categoryCtx.GetBindable();
/// <summary>
/// 绑定到侧边栏用
/// </summary>
/// <returns></returns>
public ObservableCollection<BookmarkCategory> AsObservableCollection() => categoryCtx.AsObservableCollection();

public void Remove(BookmarkItem bookmark) => bookmarkCtx.Remove(bookmark);

Expand All @@ -67,4 +72,19 @@ public class BookmarkSugarContext : IBookmarkDbContext {
}

public Task SaveAsync() => Task.Run(Save); // TODO: Thread safe???

public BookmarkCategory QueryBookmarkCategory(string foreignKey) {
Debug.Assert(isLoaded);
var category = FirstOrDefault((BookmarkCategory bc) => bc.Name == foreignKey);
if (category == null) {
category = new BookmarkCategory(foreignKey);
Add(category);
}
return category;
}

public BookmarkItem[] QueryBookmarkItems(string foreignKey) {
Debug.Assert(isLoaded);
return bookmarkCtx.Query(b => b.CategoryForeignKey == foreignKey);
}
}
56 changes: 56 additions & 0 deletions ExplorerEx/Database/SqlSugar/CachedSugarContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace ExplorerEx.Database.SqlSugar;

public class CachedSugarContext<T> : SugarContext<T> where T : class, INotifyPropertyChanged, new() {
private readonly SugarCache<T> cache;

/// <summary>
/// 如果用户调用了<see cref="AsObservableCollection"/>,生成的同时要在这里也存下来,这样的话以后用户调用<see cref="Add"/>等还得更新这个
/// </summary>
private ObservableCollection<T>? observableCollection;

public CachedSugarContext(string databaseFilename) : base(databaseFilename) {
cache = new SugarCache<T>(ConnectionClient);
}

public override async Task LoadAsync() {
await base.LoadAsync();
await Task.Run(cache.LoadDatabase);
}

public override void Save() => cache.Save();

public override Task SaveAsync() => Task.Run(cache.Save); // TODO: Thread safe???

public override void Add(T item) {
cache.Add(item);
observableCollection?.Add(item);
}

public override T? FirstOrDefault(Expression<Func<T, bool>> match) => cache.FirstOrDefault(match.Compile());

public override void Remove(T item) {
cache.Remove(item);
observableCollection?.Remove(item);
}

public override bool Contains(T item) => cache.Contains(item);

public override bool Any(Expression<Func<T, bool>> match) => cache.Any(match.Compile());

public override int Count() => cache.Count();

/// <summary>
/// 获得满足条件的项目列表
/// </summary>
/// <param name="match"></param>
/// <returns></returns>
public T[] Query(Expression<Func<T, bool>> match) => cache.Query(match.Compile());

public ObservableCollection<T> AsObservableCollection() => observableCollection ??= new ObservableCollection<T>(cache.QueryAll());
}
93 changes: 38 additions & 55 deletions ExplorerEx/Database/SqlSugar/SugarCache.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Castle.DynamicProxy;
using SqlSugar;

namespace ExplorerEx.Database.SqlSugar;
Expand All @@ -12,15 +10,13 @@ namespace ExplorerEx.Database.SqlSugar;
/// 类型缓存的基类
/// </summary>
public abstract class SugarCacheBase {
protected static readonly ProxyGenerator Generator = new();

private static readonly Dictionary<Type, SugarCacheBase> Caches = new();

protected bool IsLoaded;

protected SugarCacheBase(Type type) {
if (!type.IsClass || Caches.ContainsKey(type)) {
throw new ArgumentException();
throw new ArgumentOutOfRangeException(nameof(type));
}
Caches.Add(type, this);
}
Expand All @@ -33,7 +29,7 @@ public abstract class SugarCacheBase {
/// <typeparam name="TSub"></typeparam>
public class SugarStrategy<TSelf, TSub>
where TSelf : class, new()
where TSub : class, new() {
where TSub : class, INotifyPropertyChanged, new() {

public Action<TSelf, TSub> Action { get; }
public SugarCache<TSub> Source { get; }
Expand All @@ -45,11 +41,10 @@ public class SugarStrategy<TSelf, TSub>
}

/// <summary>
/// 需要注意的是 添加到缓存的泛型 需要将getter和setter标记为virtual才能被正确捕捉属性的更改,
/// 相反的,不标记virtual可以防止不必要的捕捉
/// 需要注意的是 添加到缓存的泛型 需要实现<see cref="INotifyPropertyChanged"/>
/// </summary>
/// <typeparam name="T"></typeparam>
public class SugarCache<T> : SugarCacheBase where T : class, new() {
public class SugarCache<T> : SugarCacheBase where T : class, INotifyPropertyChanged, new() {
protected readonly ISqlSugarClient db;

/// <summary>
Expand All @@ -66,17 +61,16 @@ public class SugarCache<T> : SugarCacheBase where T : class, new() {

private readonly object lockObj = new();

protected readonly DbModelInterceptor interceptor;

public SugarCache(ISqlSugarClient db) : base(typeof(T)) {
this.db = db;
interceptor = new DbModelInterceptor(this);
}

public void LoadDatabase() {
if (!IsLoaded) {
var interceptor = new DbModelInterceptor(this);
db.Queryable<T>().ForEach(x => locals.Add(Generator.CreateClassProxyWithTarget(x, interceptor)));
db.Queryable<T>().ForEach(x => {
x.PropertyChanged += MarkAsChanged;
locals.Add(x);
});
IsLoaded = true;
}
}
Expand All @@ -85,31 +79,14 @@ public class SugarCache<T> : SugarCacheBase where T : class, new() {

public bool Contains(T item) => locals.Contains(item);

public void ForEach(Action<T> action) {
foreach (var local in locals) {
action.Invoke(local);
}
}

public bool Any(Func<T, bool> match) => locals.Any(match);

public void Add(T item) {
// TODO: 目前是加锁,是否需要一个并行方法?(并行参考Utils.Collections.ConcurrentObservableCollection)
lock (lockObj) {
item = Generator.CreateClassProxyWithTarget(item, interceptor);
adds.Add(item);
locals.Add(item);
}
}

/// <summary>
/// 标记为已更改
/// </summary>
/// <param name="item">为Proxy</param>
private void MarkAsChanged(T item) {
lock (lockObj) {
if (!changes.Contains(item) && !deletes.Contains(item) && locals.Contains(item)) {
changes.Add(item);
if (!locals.Contains(item)) {
item.PropertyChanged += MarkAsChanged;
adds.Add(item);
locals.Add(item);
}
}
}
Expand Down Expand Up @@ -143,28 +120,34 @@ public class SugarCache<T> : SugarCacheBase where T : class, new() {
db.SaveQueues();
}

protected class DbModelInterceptor : IInterceptor {
private readonly SugarCache<T> cache;
/// <summary>
/// 一次性获取所有的本地数据,返回数组
/// </summary>
/// <returns></returns>
public T[] QueryAll() {
lock (lockObj) {
return locals.ToArray();
}
}

public DbModelInterceptor(SugarCache<T> cache) {
this.cache = cache;
/// <summary>
/// 一次性获取所有符合条件的本地数据,返回数组
/// </summary>
/// <returns></returns>
public T[] Query(Func<T, bool> match) {
lock (lockObj) {
return locals.Where(match).ToArray();
}
}

public void Intercept(IInvocation invocation) {
invocation.Proceed();
// 我认为这里不应该用Task
// 不然修改属性就是隐式的多线程
// 如果涉及高并发修改,性能压力会比较大

// 是否应该把反射提前到构造方法,需要跟踪的提前记录下来
// 这里只用一个简单的比对即可
// 至于下面的Contains和Add,目前是HashSet,性能很高
// 可以考虑使用并发Add

if (invocation.Method.Attributes.HasFlag(MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName)) {
if (invocation.Method.ReturnType == typeof(void)) {
cache.MarkAsChanged((T)invocation.Proxy); // set
}
/// <summary>
/// 标记为已更改
/// </summary>
private void MarkAsChanged(object? sender, PropertyChangedEventArgs e) {
lock (lockObj) {
var item = (T)sender!;
if (!changes.Contains(item) && !deletes.Contains(item) && locals.Contains(item)) {
changes.Add(item);
}
}
}
Expand Down
Loading

0 comments on commit 98425f3

Please sign in to comment.