优化单对象缓存的单元测试

This commit is contained in:
大石头 2024-05-12 21:16:28 +08:00
parent 4157d29161
commit d05c67a4a6
5 changed files with 208 additions and 213 deletions

View File

@ -38,7 +38,7 @@
<ItemGroup>
<PackageReference Include="IBM.Data.DB2.Core" Version="3.1.0.600" />
<PackageReference Include="MySql.Data" Version="8.4.0" />
<PackageReference Include="NewLife.Redis" Version="5.6.2024.402" />
<PackageReference Include="NewLife.Redis" Version="5.6.2024.508" />
<PackageReference Include="NewLife.Remoting" Version="2.8.2024.402" />
<PackageReference Include="NewLife.Stardust" Version="2.9.2024.402" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.4.0" />

View File

@ -1,99 +1,95 @@
using System;
using System.Collections.Generic;
namespace XCode.Cache;
namespace XCode.Cache
/// <summary>缓存基接口</summary>
public interface IEntityCacheBase
{
/// <summary>缓存基接口</summary>
public interface IEntityCacheBase
{
/// <summary>连接名</summary>
String ConnName { get; set; }
/// <summary>连接名</summary>
String ConnName { get; set; }
/// <summary>表名</summary>
String TableName { get; set; }
}
/// <summary>表名</summary>
String TableName { get; set; }
}
/// <summary>实体缓存接口</summary>
public interface IEntityCache : IEntityCacheBase
{
/// <summary>实体集合。因为涉及一个转换,数据量大时很耗性能,建议不要使用。</summary>
IList<IEntity> Entities { get; }
/// <summary>实体缓存接口</summary>
public interface IEntityCache : IEntityCacheBase
{
/// <summary>实体集合。因为涉及一个转换,数据量大时很耗性能,建议不要使用。</summary>
IList<IEntity> Entities { get; }
/// <summary>清除缓存</summary>
/// <param name="reason">清除原因</param>
/// <param name="force">强制清除下次访问阻塞等待。默认false仅置为过期下次访问异步更新</param>
void Clear(String reason, Boolean force = false);
}
/// <summary>清除缓存</summary>
/// <param name="reason">清除原因</param>
/// <param name="force">强制清除下次访问阻塞等待。默认false仅置为过期下次访问异步更新</param>
void Clear(String reason, Boolean force = false);
}
/// <summary>单对象缓存接口</summary>
public interface ISingleEntityCache : IEntityCacheBase
{
/// <summary>过期时间。单位是秒默认60秒</summary>
Int32 Expire { get; set; }
/// <summary>单对象缓存接口</summary>
public interface ISingleEntityCache : IEntityCacheBase
{
/// <summary>过期时间。单位是秒默认60秒</summary>
Int32 Expire { get; set; }
/// <summary>最大实体数。默认10000</summary>
Int32 MaxEntity { get; set; }
/// <summary>最大实体数。默认10000</summary>
Int32 MaxEntity { get; set; }
/// <summary>是否在使用缓存</summary>
Boolean Using { get; set; }
/// <summary>是否在使用缓存</summary>
Boolean Using { get; set; }
/// <summary>获取数据</summary>
/// <param name="key"></param>
/// <returns></returns>
IEntity this[Object key] { get; }
/// <summary>获取数据</summary>
/// <param name="key"></param>
/// <returns></returns>
IEntity this[Object key] { get; }
/// <summary>根据从键获取实体数据</summary>
/// <param name="slaveKey"></param>
/// <returns></returns>
IEntity GetItemWithSlaveKey(String slaveKey);
/// <summary>根据从键获取实体数据</summary>
/// <param name="slaveKey"></param>
/// <returns></returns>
IEntity GetItemWithSlaveKey(String slaveKey);
/// <summary>是否包含指定主键</summary>
/// <param name="key"></param>
/// <returns></returns>
Boolean ContainsKey(Object key);
/// <summary>是否包含指定主键</summary>
/// <param name="key"></param>
/// <returns></returns>
Boolean ContainsKey(Object key);
/// <summary>是否包含指定从键</summary>
/// <param name="key"></param>
/// <returns></returns>
Boolean ContainsSlaveKey(String key);
/// <summary>是否包含指定从键</summary>
/// <param name="key"></param>
/// <returns></returns>
Boolean ContainsSlaveKey(String key);
/// <summary>向单对象缓存添加项</summary>
/// <param name="value">实体对象</param>
/// <returns></returns>
Boolean Add(IEntity value);
/// <summary>向单对象缓存添加项</summary>
/// <param name="value">实体对象</param>
/// <returns></returns>
Boolean Add(IEntity value);
/// <summary>移除指定项</summary>
/// <param name="entity"></param>
void Remove(IEntity entity);
/// <summary>移除指定项</summary>
/// <param name="entity"></param>
void Remove(IEntity entity);
/// <summary>清除所有数据</summary>
/// <param name="reason">清除缓存原因</param>
void Clear(String reason);
}
/// <summary>清除所有数据</summary>
/// <param name="reason">清除缓存原因</param>
void Clear(String reason);
}
/// <summary></summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TEntity"></typeparam>
public interface ISingleEntityCache<TKey, TEntity> : ISingleEntityCache where TEntity : Entity<TEntity>, new()
{
/// <summary>获取数据</summary>
/// <param name="key"></param>
/// <returns></returns>
TEntity this[TKey key] { get; }
/// <summary></summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TEntity"></typeparam>
public interface ISingleEntityCache<TKey, TEntity> : ISingleEntityCache where TEntity : Entity<TEntity>, new()
{
/// <summary>获取数据</summary>
/// <param name="key"></param>
/// <returns></returns>
TEntity this[TKey key] { get; }
/// <summary>获取缓存主键的方法,默认方法为获取实体主键值</summary>
Func<TEntity, TKey> GetKeyMethod { get; set; }
/// <summary>获取缓存主键的方法,默认方法为获取实体主键值</summary>
Func<TEntity, TKey> GetKeyMethod { get; set; }
/// <summary>查找数据的方法</summary>
Func<TKey, TEntity> FindKeyMethod { get; set; }
/// <summary>查找数据的方法</summary>
Func<TKey, TEntity> FindKeyMethod { get; set; }
/// <summary>从键是否区分大小写</summary>
Boolean SlaveKeyIgnoreCase { get; set; }
/// <summary>从键是否区分大小写</summary>
Boolean SlaveKeyIgnoreCase { get; set; }
/// <summary>根据从键查找数据的方法</summary>
Func<String, TEntity> FindSlaveKeyMethod { get; set; }
/// <summary>根据从键查找数据的方法</summary>
Func<String, TEntity>? FindSlaveKeyMethod { get; set; }
/// <summary>获取缓存从键的方法,默认为空</summary>
Func<TEntity, String> GetSlaveKeyMethod { get; set; }
}
/// <summary>获取缓存从键的方法,默认为空</summary>
Func<TEntity, String>? GetSlaveKeyMethod { get; set; }
}

View File

@ -18,6 +18,7 @@ namespace XCode.Cache;
/// <typeparam name="TKey">键值类型</typeparam>
/// <typeparam name="TEntity">实体类型</typeparam>
public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntityCache<TKey, TEntity>
where TKey : notnull
where TEntity : Entity<TEntity>, new()
{
#region
@ -36,10 +37,10 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
#region
/// <summary>获取缓存主键的方法,默认方法为获取实体主键值</summary>
public Func<TEntity, TKey> GetKeyMethod { get; set; }
public Func<TEntity, TKey> GetKeyMethod { get; set; } = null!;
/// <summary>查找数据的方法</summary>
public Func<TKey, TEntity> FindKeyMethod { get; set; }
public Func<TKey, TEntity> FindKeyMethod { get; set; } = null!;
#endregion
#region
@ -47,14 +48,14 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
public Boolean SlaveKeyIgnoreCase { get; set; }
/// <summary>根据从键查找数据的方法</summary>
public Func<String, TEntity> FindSlaveKeyMethod { get; set; }
public Func<String, TEntity>? FindSlaveKeyMethod { get; set; }
/// <summary>获取缓存从键的方法,默认为空</summary>
public Func<TEntity, String> GetSlaveKeyMethod { get; set; }
public Func<TEntity, String>? GetSlaveKeyMethod { get; set; }
#endregion
#region
/// <summary>实例化一个实体缓存</summary>
/// <summary>实例化一个单对象缓存</summary>
public SingleEntityCache()
{
var exp = XCodeSetting.Current.SingleCacheExpire;
@ -90,7 +91,7 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
#endregion
#region
private TimerX _Timer;
private TimerX? _Timer;
private void StartTimer()
{
if (_Timer == null)
@ -167,16 +168,16 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
#region
/// <summary>缓存对象</summary>
class CacheItem
class CacheItem(TKey key, String? slaveKey)
{
/// <summary>键</summary>
public TKey Key { get; set; }
public TKey Key { get; set; } = key;
/// <summary>从键</summary>
public String SlaveKey { get; set; }
public String? SlaveKey { get; set; } = slaveKey;
/// <summary>实体</summary>
public TEntity Entity { get; set; }
public TEntity? Entity { get; set; }
/// <summary>访问时间</summary>
public DateTime VisitTime { get; set; }
@ -184,9 +185,6 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
/// <summary>缓存过期时间</summary>
public DateTime ExpireTime { get; set; }
///// <summary>是否已经过期</summary>
//public Boolean Expired => ExpireTime <= TimerX.Now;
public void SetEntity(TEntity entity, Int32 expire)
{
Entity = entity;
@ -207,7 +205,7 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
/// <summary>单对象缓存</summary>
private readonly ConcurrentDictionary<TKey, CacheItem> Entities = new();
private ConcurrentDictionary<String, CacheItem> _SlaveEntities;
private ConcurrentDictionary<String, CacheItem>? _SlaveEntities;
/// <summary>单对象缓存,从键查询使用</summary>
private ConcurrentDictionary<String, CacheItem> SlaveEntities
{
@ -363,12 +361,8 @@ public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntit
var skey = entity == null ? null : GetSlaveKeyMethod?.Invoke(entity);
var item = new CacheItem
{
Key = key,
SlaveKey = skey
};
item.SetEntity(entity, Expire);
var item = new CacheItem(key, skey);
item.SetEntity(entity!, Expire);
//var es = Entities;
//// 新增或更新

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Threading;
using NewLife.Log;
using XCode.Cache;
@ -33,23 +34,28 @@ public class SingleEntityCacheTests
[Fact]
public void TestKey()
{
var list = User.FindAll(null, null, null, 0, 1);
var id = list.FirstOrDefault().ID;
XTrace.WriteLine("准备在User上测试单对象缓存ID={0} ", id);
var cache = new SingleEntityCache<Int32, User> { Expire = 1 };
// 首次访问
XTrace.WriteLine("首次访问");
var user = cache[1];
var user = cache[id];
Assert.Equal(0, cache.Success);
// 再次访问
XTrace.WriteLine("再次访问");
var user2 = cache[1];
var user2 = cache[id];
Assert.Equal(1, cache.Success);
Thread.Sleep(cache.Expire * 1000 + 10);
// 三次访问
XTrace.WriteLine("三次访问");
var user3 = cache[1];
var user3 = cache[id];
Assert.Equal(2, cache.Success);
}

View File

@ -9,150 +9,149 @@ using XCode.DataAccessLayer;
using XCode.Membership;
using Xunit;
namespace XUnitTest.XCode.DataAccessLayer
namespace XUnitTest.XCode.DataAccessLayer;
[TestCaseOrderer("NewLife.UnitTest.DefaultOrderer", "NewLife.UnitTest")]
public class OracleTests
{
[TestCaseOrderer("NewLife.UnitTest.DefaultOrderer", "NewLife.UnitTest")]
public class OracleTests
private String _ConnStr = "Data Source=Tcp://127.0.0.1/ORCL;User Id=scott;Password=tiger";
public OracleTests()
{
private String _ConnStr = "Data Source=Tcp://127.0.0.1/ORCL;User Id=scott;Password=tiger";
var f = "Config\\oracle.config".GetFullPath();
if (File.Exists(f))
_ConnStr = File.ReadAllText(f);
else
File.WriteAllText(f, _ConnStr);
}
public OracleTests()
{
var f = "Config\\oracle.config".GetFullPath();
if (File.Exists(f))
_ConnStr = File.ReadAllText(f);
else
File.WriteAllText(f, _ConnStr);
}
[Fact]
public void InitTest()
{
var db = DbFactory.Create(DatabaseType.Oracle);
Assert.NotNull(db);
[Fact]
public void InitTest()
{
var db = DbFactory.Create(DatabaseType.Oracle);
Assert.NotNull(db);
var factory = db.Factory;
Assert.NotNull(factory);
var factory = db.Factory;
Assert.NotNull(factory);
var conn = factory.CreateConnection();
Assert.NotNull(conn);
var conn = factory.CreateConnection();
Assert.NotNull(conn);
var cmd = factory.CreateCommand();
Assert.NotNull(cmd);
var cmd = factory.CreateCommand();
Assert.NotNull(cmd);
var adp = factory.CreateDataAdapter();
Assert.NotNull(adp);
var adp = factory.CreateDataAdapter();
Assert.NotNull(adp);
var dp = factory.CreateParameter();
Assert.NotNull(dp);
}
var dp = factory.CreateParameter();
Assert.NotNull(dp);
}
[Fact(Skip = "跳过")]
public void ConnectTest()
{
var db = DbFactory.Create(DatabaseType.Oracle);
var factory = db.Factory;
[Fact(Skip = "跳过")]
public void ConnectTest()
{
var db = DbFactory.Create(DatabaseType.Oracle);
var factory = db.Factory;
var conn = factory.CreateConnection();
conn.ConnectionString = "Server=localhost;Port=5236;Database=dameng;user=SYSDBA;password=SYSDBA";
conn.Open();
}
var conn = factory.CreateConnection();
conn.ConnectionString = "Server=localhost;Port=5236;Database=dameng;user=SYSDBA;password=SYSDBA";
conn.Open();
}
[Fact(Skip = "跳过")]
public void DALTest()
{
DAL.AddConnStr("Oracle", _ConnStr, null, "Oracle");
var dal = DAL.Create("Oracle");
Assert.NotNull(dal);
Assert.Equal("Oracle", dal.ConnName);
Assert.Equal(DatabaseType.Oracle, dal.DbType);
[Fact(Skip = "跳过")]
public void DALTest()
{
DAL.AddConnStr("Oracle", _ConnStr, null, "Oracle");
var dal = DAL.Create("Oracle");
Assert.NotNull(dal);
Assert.Equal("Oracle", dal.ConnName);
Assert.Equal(DatabaseType.Oracle, dal.DbType);
var db = dal.Db;
var connstr = db.ConnectionString;
Assert.Equal("dameng", db.Owner);
Assert.Equal("Server=localhost;Port=5236;user=SYSDBA;password=SYSDBA", connstr);
var db = dal.Db;
var connstr = db.ConnectionString;
Assert.Equal("dameng", db.Owner);
Assert.Equal("Server=localhost;Port=5236;user=SYSDBA;password=SYSDBA", connstr);
var ver = db.ServerVersion;
Assert.NotEmpty(ver);
}
var ver = db.ServerVersion;
Assert.NotEmpty(ver);
}
[Fact(Skip = "跳过")]
public void MetaTest()
{
DAL.AddConnStr("Oracle", _ConnStr, null, "Oracle");
var dal = DAL.Create("Oracle");
[Fact(Skip = "跳过")]
public void MetaTest()
{
DAL.AddConnStr("Oracle", _ConnStr, null, "Oracle");
var dal = DAL.Create("Oracle");
var tables = dal.Tables;
Assert.NotNull(tables);
Assert.True(tables.Count > 0);
}
var tables = dal.Tables;
Assert.NotNull(tables);
Assert.True(tables.Count > 0);
}
[Fact(Skip = "跳过")]
public void SelectTest()
{
//DAL.AddConnStr("Membership", _ConnStr, null, "Oracle");
DAL.AddConnStr("Oracle", _ConnStr, null, "Oracle");
[Fact(Skip = "跳过")]
public void SelectTest()
{
//DAL.AddConnStr("Membership", _ConnStr, null, "Oracle");
DAL.AddConnStr("Oracle", _ConnStr, null, "Oracle");
Role.Meta.ConnName = "Oracle";
Area.Meta.ConnName = "Oracle";
Role.Meta.ConnName = "Oracle";
Area.Meta.ConnName = "Oracle";
Role.Meta.Session.InitData();
Role.Meta.Session.InitData();
var count = Role.Meta.Count;
Assert.True(count > 0);
var count = Role.Meta.Count;
Assert.True(count > 0);
var list = Role.FindAll();
Assert.Equal(4, list.Count);
var list = Role.FindAll();
Assert.Equal(4, list.Count);
var list2 = Role.FindAll(Role._.Name == "管理员");
Assert.Equal(1, list2.Count);
var list2 = Role.FindAll(Role._.Name == "管理员");
Assert.Equal(1, list2.Count);
var list3 = Role.Search("用户", null);
Assert.Equal(2, list3.Count);
var list3 = Role.Search("用户", null);
Assert.Equal(2, list3.Count);
// 来个耗时操作,把前面堵住
Area.FetchAndSave();
}
// 来个耗时操作,把前面堵住
Area.FetchAndSave();
}
[Fact(Skip = "跳过")]
public void PositiveAndNegative()
{
var connName = GetType().Name;
DAL.AddConnStr(connName, _ConnStr, null, "Oracle");
var dal = DAL.Create(connName);
[Fact(Skip = "跳过")]
public void PositiveAndNegative()
{
var connName = GetType().Name;
DAL.AddConnStr(connName, _ConnStr, null, "Oracle");
var dal = DAL.Create(connName);
var table = User.Meta.Table.DataTable.Clone() as IDataTable;
table.TableName = $"user_{Rand.Next(1000, 10000)}";
var table = User.Meta.Table.DataTable.Clone() as IDataTable;
table.TableName = $"user_{Rand.Next(1000, 10000)}";
dal.SetTables(table);
dal.SetTables(table);
var tableNames = dal.GetTableNames();
XTrace.WriteLine("tableNames: {0}", tableNames.Join());
Assert.Contains(table.TableName, tableNames);
var tableNames = dal.GetTableNames();
XTrace.WriteLine("tableNames: {0}", tableNames.Join());
Assert.Contains(table.TableName, tableNames);
var tables = dal.Tables;
XTrace.WriteLine("tables: {0}", tables.Join());
Assert.Contains(tables, t => t.TableName == table.TableName);
var tables = dal.Tables;
XTrace.WriteLine("tables: {0}", tables.Join());
Assert.Contains(tables, t => t.TableName == table.TableName);
dal.Db.CreateMetaData().SetSchema(DDLSchema.DropTable, table);
dal.Db.CreateMetaData().SetSchema(DDLSchema.DropTable, table);
tableNames = dal.GetTableNames();
XTrace.WriteLine("tableNames: {0}", tableNames.Join());
Assert.DoesNotContain(table.TableName, tableNames);
}
tableNames = dal.GetTableNames();
XTrace.WriteLine("tableNames: {0}", tableNames.Join());
Assert.DoesNotContain(table.TableName, tableNames);
}
[Fact]
public void CreateParameterForByteArray()
{
var connName = GetType().Name;
DAL.AddConnStr(connName, _ConnStr, null, "Oracle");
var dal = DAL.Create(connName);
[Fact]
public void CreateParameterForByteArray()
{
var connName = GetType().Name;
DAL.AddConnStr(connName, _ConnStr, null, "Oracle");
var dal = DAL.Create(connName);
var data = "NewLife".GetBytes();
var dp = dal.Db.CreateParameter("data", data);
var data = "NewLife".GetBytes();
var dp = dal.Db.CreateParameter("data", data);
Assert.NotNull(dp);
Assert.Equal(DbType.Binary, dp.DbType);
}
Assert.NotNull(dp);
Assert.Equal(DbType.Binary, dp.DbType);
}
}