更改通知
我们可以通过实现IMessageQueue并在对象上下文提供者中返回其实例并为某个实体型配置要通知的动作而实现更改通知。
实体模型
public class Student {
/// <summary>
///入学年
/// </summary>
private int _addmisionYear;
/// <summary>
///学生姓名
/// </summary>
private string _name;
/// <summary>
///学生ID
/// </summary>
private string _studentId;
/// <summary>
///学生ID
/// </summary>
public string StudentId
{
get => _studentId;
set => _studentId = value;
}
/// <summary>
///学生姓名
/// </summary>
public string Name
{
get => _name;
set => _name = value;
}
/// <summary>
///入学年
/// </summary>
public int AddmisionYear
{
get => _addmisionYear;
set => _addmisionYear = value;
}
}
实现IMessageQueue
/// <summary>
/// 消息队列 用于发送/接收消息
/// </summary>
public class MessageQueue : IMessageQueue
{
/// <summary>
/// 用于模拟消息队列的Txt文件路径
/// </summary>
private readonly string _path;
/// <summary>
/// 构造消息队列对象
/// </summary>
public MessageQueue()
{
_path = $"{Directory.GetCurrentDirectory()}\\TestQueue.txt";
}
/// <summary>
/// 用字节序列的形式将消息发送至消息队列。
/// </summary>
/// <param name="msg">要发送的消息对象。</param>
public void SendMessage(byte[] msg)
{
var fileStream = new FileStream(_path, FileMode.OpenOrCreate);
var writer = new StreamWriter(fileStream);
writer.WriteLine(Convert.ToBase64String(msg));
writer.Flush();
writer.Close();
}
/// <summary>
/// 用字符序列的形式将消息发送至消息队列。
/// </summary>
/// <param name="msg">要发送的消息对象。</param>
public void SendMessage(string msg)
{
var bytes = Encoding.UTF8.GetBytes(msg);
SendMessage(bytes);
}
}
开启变更通知
protected override void CreateModel(ModelBuilder modelBuilder)
{
//注册学生实体
var studentConfiguration = modelBuilder.Entity<Student>();
//配置要通知的属性
studentConfiguration.HasNoticeAttributes(new List<string>(){"Name"});
//是否在创建时通知
studentConfiguration.HasNotifyCreation(true);
//是否在修改时通知
studentConfiguration.HasNotifyUpdate(true);
//是否在删除时通知
studentConfiguration.HasNotifyDeletion(true);
}
在这些配置中,我们注册了Student作为实体模型。使用HasNoticeAttributes方法为Student配置了要通知的属性(Name),剩余的三个方法是指示在实体发生何种改变时(创建,删除,修改)进行通知的发送。
在新建对象并使用Attach方法附加至Objetset后,SaveChanges会触发Creation类型的ObjectChangeNotice;在修改对象后SaveChanges会触发Update类型的ObjectChangeNotice,在Remove后SaveChanges会触发Deletion类型的ObjectChangeNotice。 ObjectChangeNotice是一个结构,会被序列化成二进制形式后调用实现了IMessageQueue的MessageQueue的SendMessage(byte[] msg)方法,此结构的定义如下:
/// <summary>
/// 对象变更通知数据结构
/// </summary>
[Serializable]
public struct ObjectChangeNotice
{
/// <summary>
/// 对象的属性及其取值
/// </summary>
public List<ObjectAttributeValue> Attributes { get; set; }
/// <summary>
/// 变更行为,可以取“Creation”、“Update”、“Deletion”三个值
/// </summary>
public string ChangeAction { get; set; }
/// <summary>
/// 对象的标识
/// </summary>
public List<ObjectAttributeValue> ObjectKey { get; set; }
/// <summary>
/// 对象类型的名称
/// </summary>
public string ObjectType { get; set; }
/// <summary>
/// 对象类型的命名空间
/// </summary>
public string ObjectTypeNamespace { get; set; }
/// <summary>
/// 转换为字符串表示形式
/// </summary>
/// <returns></returns>
public override string ToString()
{
return
$"ObjectChangeNotice:{Attributes-[{string.Join(",", Attributes)}],ChangeAction-\"{ChangeAction}\",ObjectType-\"{ObjectType}\",ObjectTypeNamespace-\"{ObjectTypeNamespace}\"}";
}
}
此通知是使用BinaryFormatter序列化的,其中属性访问器的具体定义可以参考注释。
就地修改通知
使用直接修改方法则会发送DirectlyChangeNotice通知,使用Delete方法会发送eDirectlyChangeType.Delete类型的DirectlyChangeNotice;使用SetAttribute方法会发送eDirectlyChangeType.Update类型的DirectlyChangeNotice;使用IncreaseAttribute方法会发送eDirectlyChangeType.Increment类型的DirectlyChangeNotice。 DirectlyChangeNotice是一个结构,会被序列化成二进制形式后调用实现了IMessageQueue的SendMessage(byte[] msg)方法,此结构的定义如下:
/// <summary>
/// 就地修改通知
/// </summary>
[Serializable]
public struct DirectlyChangeNotice
{
/// <summary>
/// 筛选条件表达式
/// </summary>
public string Expression { get; set; }
/// <summary>
/// 对象类型名称
/// </summary>
public string ObjectType { get; set; }
/// <summary>
/// 对象类型的命名空间
/// </summary>
public string ObjectTypeNamespace { get; set; }
/// <summary>
/// 新值键值对
/// </summary>
public Dictionary<string, object> NewValues { get; set; }
/// <summary>
/// 就地修改类型
/// </summary>
public eDirectlyChangeType DirectlyChangeType { get; set; }
}
此通知是使用BinaryFormatter序列化的,其中属性访问器的具体定义可以参考注释。
这些通知会在我们自定义的实现了IMessageQueue的MessageQueue的 SendMessage(byte[])方法中根据我们的逻辑发送到指定的接收者(如消息队列等中间件)里,之后只要从这些接收者内取出即可获取到这些通知。
应用实例
我们依然用上述实体模型演示,要实现更改通知我们仅需要在ContextConfigProvider中重写GetNoticeQueue()方法,并在CreateModel()中开启通知功能即可:
public class ModifyNoticeConfiguration : MySqlContextConfigProvider
{
/// <summary>由派生类实现,获取数据库连接字符串。</summary>
protected override string ConnectionString => Configuration.MySqlConnectionString;
/// <summary>获取一个值,该值指示是否启用存储结构映射</summary>
protected override bool EnableStructMapping => true;
/// <summary>
///获取用于发送对象变更通知的消息队列。
/// </summary>
/// <returns>如果要发送对象变更通知,返回一个消息队列实例;否则返回null。</returns>
protected override IMessageQueue GetNoticeQueue()
{
//返回一个用于操作消息队列的 实现IMessageQueue接口的对象即可
return new MessageQueue();
}
/// <summary>使用指定的建模器创建对象数据模型。</summary>
/// <param name="modelBuilder"></param>
protected override void CreateModel(ModelBuilder modelBuilder)
{
//配置学生实体型
var studentEntity = modelBuilder.Entity<Student>();
//配置主键
studentEntity.HasKeyAttribute(p => p.StudentId).HasKeyIsSelfIncreased(false);
//配置要通知的属性 这些属性的值会包含在通知对象中
studentEntity.HasNoticeAttributes(new List<string> { "StudentId", "Name", "AddmisionYear" });
//配置要通知的行为 此处设为通知创建Creation 删除Deletion 修改Update
studentEntity.HasNotifyCreation(true);
studentEntity.HasNotifyDeletion(true);
studentEntity.HasNotifyUpdate(true);
//同时 还需要指定这些消息的接收者 需要覆写IMessageQueue GetNoticeQueue()方法 见本类25行
}
}