XAF使用数据库访问层缓存的提升性能
1、XAF使用Cache缓存原始帖
https://supportcenter.devexpress.com/ticket/details/k18356/how-to-use-xpo-data-layer-caching-in-xaf
2、XAF使用会话层缓存
会话层缓存 默认情况下,XPO在会话级别缓存记录。因为大多数XAF根视图都有自己的会话,所以每个根视图都有自己的缓存。当视图关闭或其对象空间被重新加载时,该缓存被重置。此外,大多数数据库系统都提供了一种高级的缓存机制,可以更快地执行相同SQL查询的后续调用。在大多数情况下,这种功能足以实现良好的性能。如果数据库服务器连接延迟很长,并且有大量重复查询,那么在数据存储级别实现缓存可能会有所帮助。
数据层缓存要启用数据存储级缓存,请将应用程序连接到缓存的数据存储。在这种情况下,应用程序缓存数据库查询及其结果。如果应用程序第二次执行相同的查询,缓存的数据将从内存中加载。DataCacheNode和DataCacheNodeLocal对象执行缓存。将这些对象连接到一个共享的DataCacheRoot对象。DataCacheRoot同步不同缓存节点中所做的更改。因此,一次只能将一个根目录连接到数据库。您需要通过它传递所有的数据修改。若要重置或清除数据存储表上的任何缓存信息(如表更新信息和缓存的查询结果),请使用NotifyDirtyTables或reset()方法(请参见有关仅为特定类型重置数据存储缓存的问题中的代码示例)。有关内置XPO缓存策略及其差异的更多信息,请参见会话管理和缓存。
3、示例代码方法:
3.1. 在解决方案名称中项目,使用以下代码添加CachedDataStoreProvider.cs文件.
public class CachedDataStoreProvider : ConnectionStringDataStoreProvider, IXpoDataStoreProvider {
static CachedDataStoreProvider() {
Factory = () => default;
CreateStore = () => default;
CustomCreateUpdatingStore = () => default;
}
private static readonly Lazy<CachedDataStoreProvider> Lazy = new(()
=> Factory() ?? new CachedDataStoreProvider(StaticConnectionString));
public static string StaticConnectionString { get; set; }
public static Func<CachedDataStoreProvider> Factory { get; set; }
private static IDisposable[] _rootDisposableObjects;
private static DataCacheRoot _root;
public static CachedDataStoreProvider Instance => Lazy.Value;
public CachedDataStoreProvider(string connectionString) : base(connectionString) {
}
public static readonly Func<(bool allowUpdateSchema, IDisposable[] rootDisposables, IDataStore dataStore)> CustomCreateUpdatingStore;
public static readonly Func<(IDisposable[] rootDisposables, IDataStore dataStore)> CreateStore;
public new IDataStore CreateWorkingStore(out IDisposable[] disposableObjects)
=> ((IXpoDataStoreProvider) this).CreateWorkingStore(out disposableObjects);
IDataStore IXpoDataStoreProvider.CreateUpdatingStore(bool allowUpdateSchema,
out IDisposable[] disposableObjects) {
var store = CustomCreateUpdatingStore();
if (store.IsDefaultValue()) {
return base.CreateUpdatingStore(allowUpdateSchema, out disposableObjects);
}
disposableObjects = store.rootDisposables;
return store.dataStore;
}
IDataStore IXpoDataStoreProvider.CreateWorkingStore(out IDisposable[] disposableObjects) {
if (_root == null) {
var tuple = CreateStore();
IDataStore baseDataStore;
if (tuple.IsDefaultValue()) {
baseDataStore = base.CreateWorkingStore(out _rootDisposableObjects);
}
else {
baseDataStore = tuple.dataStore;
_rootDisposableObjects = tuple.rootDisposables;
}
_root = new DataCacheRoot(baseDataStore);
}
disposableObjects = Array.Empty<IDisposable>();
return new DataCacheNode(_root);
}
public static void ResetDataCacheRoot() {
_root = null;
if (_rootDisposableObjects != null) {
foreach (var disposableObject in _rootDisposableObjects) disposableObject.Dispose();
_rootDisposableObjects = null;
}
}
}
3.2. 在启动中ConfigureServices
增加下面代码:
1.netcore 6.0之上
public void ConfigureServices(IServiceCollection services)
{
// ... services.AddSingleton<IXpoDataStoreProvider>((serviceProvider) => { string connectionString = null;
if(Configuration.GetConnectionString("ConnectionString") != null)
{ connectionString = Configuration.GetConnectionString("ConnectionString"); }
builder.ObjectSpaceProviders
.AddSecuredXpo((serviceProvider, options) => {
//options.ConnectionString = GetConnectionString(serviceProvider);
//var useMemoryStore = false;
//options.EnablePoolingInConnectionString = !useMemoryStore;
//options.ThreadSafe = true;
//options.UseSharedDataStoreProvider = true;
options.ThreadSafe = true;
var cachedDataStoreProvider = serviceProvider.GetRequiredService<IXpoDataStoreProvider>();
ArgumentNullException.ThrowIfNull(cachedDataStoreProvider);
options.UseCustomDataStoreProvider(cachedDataStoreProvider);
})
.AddNonPersistent();
2. .netcore 5下等:
protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) { args.ObjectSpaceProvider = new CachedObjectSpaceProvider(args.ConnectionString, args.Connection); }
4.使用SqlDependency
需要注意的是:这个缓存只能在web中使用,或者在如果你有多个web服务器连接到一台DB时,也是不行的。
如有两台WEB,A,B 当A做了修改数据行为后,B服务器并不知道这个数据库中的数据已经修改,所以B仍去缓存中读取数据。
同样的,Win程序的缓存是在客户端电脑上的,所以也像多WEB一样有问题。
我搜索了官方的解决方案:
当使用了SqlServer时,可以用Service Broker 查询通知服务,进行通知客户端。这是一个方向。由于我考虑兼容性,比如用oracle或是别的库时也想有这个样的功能,所以没有用这个。
当然,如果你的web应用只是在一台服务器上部署就不用考虑这个问题了。
//_root = new DataCacheRoot(baseDataStore);
//sql conn add Persist Security Info=true
//ALTER DATABASE DatabaseName SET NEW_BROKER WITH ROLLBACK IMMEDIATE;
//ALTER DATABASE Databasename SET ENABLE_BROKER;
_root = MSSql2005SqlDependencyCacheRoot.CreateSqlDependencyCacheRoot((MSSqlConnectionProvider)baseDataStore, cacheConfig, out _rootDisposableObjects);
5.在efcore中如何使用sqldependency进行缓存
https://github.com/dyatchenko/ServiceBrokerListener
监听变更,进行缓存管理
6.在mysql中使用Binlog监控日志变更
https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor