解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
7.98 KiB
X
using NewLife.Data;
using NewLife.Reflection;

namespace NewLife.Messaging;

/// <summary>消息种类。与协议状态位一一对应:00 请求 / 01 单向 / 10 响应 / 11 响应+错误</summary>
/// <remarks>
/// 值即协议位(头部高 2 位 mode),请求-响应语义的完整分类。
/// 仅带状态位协议的消息使用方向字段;无状态位协议(长度字段/分隔符等)不使用,保持默认的请求即可。
/// </remarks>
public enum MessageKinds
{
    /// <summary>请求</summary>
    Request = 0,

    /// <summary>单向请求。不需要等待响应</summary>
    OneWay = 1,

    /// <summary>响应</summary>
    Response = 2,

    /// <summary>响应+错误</summary>
    Error = 3,
}

/// <summary>消息契约。纯数据与负载读写;帧格式的解析与构建由 <see cref="IMessageCodec"/> 承担</summary>
/// <remarks>
/// <para>消息接口定义消息的最小契约:负载读写(<see cref="Payload"/>/<see cref="Body"/>)与方向语义(<see cref="Kind"/>/<see cref="Reply"/>)。</para>
/// <para>消息为纯数据载体,不感知帧字节:定界/解析/构建由协议对象(<see cref="IMessageCodec"/>)统一处理。</para>
/// <para>协议字段的读写属于消息类:带帧字段的消息子类公开 TryParse/WriteHeader 方法,由 codec 委托调用(参考 <see cref="DefaultMessage"/> 与 <see cref="WsMessage"/>)。</para>
/// <para>实现类须自行声明线程安全性(本接口不承诺线程安全;<see cref="Message"/> 及其子类为非线程安全)。</para>
/// </remarks>
public interface IMessage : IDisposable
{
    /// <summary>负载数据包。消息体的句柄视图:整帧/发送模式为底层数据包(零拷贝,所有权随消息);流式模式为 null</summary>
    /// <remarks>
    /// 负载为数据包(借阅视图或拥有帧/链)。拥有帧的所有权随消息持有:消息 <see cref="IDisposable.Dispose"/>
    /// 时唯一归还(<see cref="IPacket"/> 实现 Dispose 归还池化缓冲);
    /// 借阅视图(ArrayPacket 等)无所有权,仅在本轮同步链路内有效。写路径统一走 <see cref="SetBody"/>(null 表示所有权转移、不归还)。
    /// 流式消息体经 <see cref="Body"/> 读取,本属性为 null。
    /// </remarks>
    IPacket? Payload { get; }

    /// <summary>消息体。整帧模式为内存视图,头先行模式为限长流式读取器;未设置时为 null</summary>
    LimitedReader? Body { get; }

    /// <summary>消息种类。请求/单向/响应/响应+错误,与协议状态位一一对应</summary>
    /// <remarks>无状态位协议不使用本字段,保持默认的请求即可</remarks>
    MessageKinds Kind { get; set; }

    /// <summary>是否为应答消息(响应或响应+错误)。配对交付与流式物化的判断入口</summary>
    Boolean Reply { get; }

    /// <summary>根据请求创建配对的响应消息(继承序列号等配对键)</summary>
    /// <returns>响应消息实例;当前消息是应答(<see cref="Reply"/>)或协议不支持回复时返回 null</returns>
    IMessage? CreateReply();

    /// <summary>设置消息体(整帧/发送路径)。拥有句柄所有权随消息,Dispose 时归还;传入 null 表示放弃持有(所有权转移给调用方或构建结果),不归还</summary>
    /// <param name="packet">消息体数据包</param>
    void SetBody(IPacket? packet);

    /// <summary>绑定流式消息体(帧层专用)。头部先行解析后由帧泵调用</summary>
    /// <param name="body">限长读取器;null 表示清除流式体绑定(不归还其资源,由帧层管理)</param>
    void BindBody(LimitedReader body);
}

/// <summary>消息基类。负载协作面的基础实现;帧格式由 <see cref="IMessageCodec"/> 承担</summary>
/// <remarks>
/// <para>消息只承载头部字段与负载(<see cref="SetBody"/>/<see cref="BindBody"/> 由帧层在解析后绑定),不感知帧字节。</para>
/// <para>方向字段对无状态位协议无意义(保持默认请求);<see cref="Reply"/> 与 <see cref="OneWay"/> 为 <see cref="Kind"/> 的便捷视图。</para>
/// </remarks>
public class Message : IMessage
{
    #region 属性
    /// <summary>消息体。整帧模式为内存视图,头先行模式为限长流式读取器;未设置时为 null</summary>
    public LimitedReader? Body { get; private set; }

    /// <summary>负载数据包。消息体的句柄视图:整帧/发送模式为底层数据包(零拷贝,所有权随消息);流式模式为 null</summary>
    /// <remarks>写路径统一走 <see cref="SetBody"/>;置 null 表示所有权转移、不归还。</remarks>
    public IPacket? Payload { get; private set; }

    /// <summary>消息种类。请求/单向/响应/响应+错误,与协议状态位一一对应</summary>
    public MessageKinds Kind { get; set; }

    /// <summary>是否为应答消息(响应或响应+错误)。配对交付与流式物化的判断入口</summary>
    public Boolean Reply
    {
        get => Kind is MessageKinds.Response or MessageKinds.Error;
        set => Kind = value ? MessageKinds.Response : MessageKinds.Request;
    }

    /// <summary>是否为单向请求(无需应答)</summary>
    public Boolean OneWay
    {
        get => Kind == MessageKinds.OneWay;
        set => Kind = value ? MessageKinds.OneWay : MessageKinds.Request;
    }
    #endregion

    #region 构造
    /// <summary>销毁。回收数据包到内存池</summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>释放资源</summary>
    /// <param name="disposing">是否释放托管资源</param>
    protected virtual void Dispose(Boolean disposing)
    {
        if (disposing)
        {
            // 拥有帧时归还池化缓冲;借阅视图(ArrayPacket)Dispose 无操作;流式体的管道端由帧层管理
            Payload.TryDispose();
            Payload = null;
            Body = null;
        }
    }
    #endregion

    #region 方法
    /// <summary>创建当前类型的新实例</summary>
    /// <remarks>子类可重写以避免反射开销,或实现对象池化复用</remarks>
    /// <returns>新的消息实例</returns>
    protected virtual Message CreateInstance()
    {
        var type = GetType();
        if (type == typeof(Message)) return new Message();

        if (type.CreateInstance() is Message msg) return msg;

        throw new InvalidOperationException($"Cannot create an instance of type [{type.FullName}]");
    }

    /// <summary>根据请求创建配对的响应消息。基类不支持回复,返回 null;带配对键的协议子类重写</summary>
    /// <returns>响应消息实例;基类恒返回 null</returns>
    public virtual IMessage? CreateReply() => null;

    /// <summary>设置消息体(整帧/发送路径)</summary>
    /// <param name="packet">消息体数据包;置 null 表示放弃持有(所有权已转移给构建结果或调用方),不归还</param>
    public void SetBody(IPacket? packet)
    {
        var old = Payload;

        // 置 null 表示放弃持有:负载已随调用方交付,消息不再持有,也不能归还
        if (packet == null)
        {
            Payload = null;
            Body = null;
            return;
        }

        Payload = packet;
        Body = new LimitedReader(packet, 0, packet.Total);

        // 替换旧体时归还其拥有帧
        if (old != null && !ReferenceEquals(old, packet)) old.TryDispose();
    }

    /// <summary>绑定流式消息体(帧层专用)。头部先行解析后由帧泵调用</summary>
    /// <param name="body">限长读取器</param>
    public void BindBody(LimitedReader body)
    {
        var old = Payload;
        Payload = null;
        Body = body;
        old?.TryDispose();
    }
    #endregion
}