Task.Yield会导致强制捕获上下文,虽然会在另一个线程执行,但在UI线程中可能无法抢占上下文导致死锁
大石头 authored at 2019-07-10 15:21:23
5.70 KiB
X
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using NewLife.Data;
using NewLife.Log;
using NewLife.Net;
using NewLife.Reflection;
using NewLife.Remoting;
using NewLife.Security;
using NewLife.Serialization;

namespace XCode.Service
{
    /// <summary>数据客户端</summary>
    public class DbClient : ApiClient
    {
        #region 属性
        /// <summary>数据库连接</summary>
        public String Db { get; set; }

        /// <summary>用户名</summary>
        public String UserName { get; set; }

        /// <summary>密码</summary>
        public String Password { get; set; }

        /// <summary>最后一次登录成功后的消息</summary>
        public LoginInfo Info { get; private set; }
        #endregion

        #region 方法
        /// <summary>实例化</summary>
        public DbClient()
        {
#if DEBUG
            Log = XTrace.Log;
            EncoderLog = XTrace.Log;
#endif
        }

        /// <summary>实例化</summary>
        /// <param name="uri"></param>
        public DbClient(String uri)
        {
            if (!uri.IsNullOrEmpty())
            {
                var u = new Uri(uri);

                Servers = new[] { "{2}://{0}:{1}".F(u.Host, u.Port, u.Scheme) };

                Db = u.PathAndQuery.Split("/").FirstOrDefault();
                var us = u.UserInfo.Split(":");
                UserName = us.Length > 0 ? us[0] : null;
                Password = us.Length > 1 ? us[1] : null;
            }
        }
        #endregion

        #region 登录
        /// <summary>连接后自动登录</summary>
        /// <param name="client">客户端</param>
        /// <param name="force">强制登录</param>
        protected override async Task<Object> OnLoginAsync(ISocketClient client, Boolean force)
        {
            var cookie = Rand.NextString(16);
            var pass2 = cookie.GetBytes().RC4(Password.GetBytes()).ToBase64();

            var rs = await InvokeWithClientAsync<LoginInfo>(client, "Db/Login", new { Db, UserName, pass = pass2, cookie }).ConfigureAwait(false);
            if (Setting.Current.Debug) XTrace.WriteLine("登录{0}成功!{1}", Servers.FirstOrDefault(), rs.ToJson());

            return Info = rs;
        }
        #endregion

        #region 核心方法
        ///// <summary>异步连接</summary>
        ///// <returns></returns>
        //public async Task<LoginInfo> LoginAsync() => await LoginAsync(Db, UserName, Password);

        ///// <summary>异步登录</summary>
        ///// <param name="db">要访问的数据库</param>
        ///// <param name="user">用户名</param>
        ///// <param name="pass">密码</param>
        ///// <returns></returns>
        //protected async Task<LoginInfo> LoginAsync(String db, String user, String pass)
        //{
        //    var cookie = Rand.NextString(16);
        //    var pass2 = cookie.GetBytes().RC4(pass.GetBytes()).ToBase64();

        //    var rs = await InvokeAsync<LoginInfo>("Db/Login", new { db, user, pass = pass2, cookie });
        //    if (Setting.Current.Debug) XTrace.WriteLine("登录{0}成功!{1}", Servers.FirstOrDefault(), rs.ToJson());

        //    return rs;
        //}

        /// <summary>异步查询</summary>
        /// <param name="sql">语句</param>
        /// <param name="ps">参数集合</param>
        /// <returns></returns>
        public async Task<DbTable> QueryAsync(String sql, IDictionary<String, Object> ps = null)
        {
            var arg = Encode(sql, ps);

            var rs = await InvokeAsync<Packet>("Db/Query", arg).ConfigureAwait(false);
            //if (rs == null || rs.Total == 0) return null;

            var ds = new DbTable();
            ds.Read(rs);

            return ds;
        }

        /// <summary>异步查数据表总记录数</summary>
        /// <remarks>借助索引快速查询,但略有偏差</remarks>
        /// <param name="tableName">数据表</param>
        /// <returns></returns>
        public async Task<Int64> QueryCountAsync(String tableName)
        {
            //var arg = Encode(tableName, null);

            return await InvokeAsync<Int64>("Db/QueryCount", new { tableName }).ConfigureAwait(false);
        }

        /// <summary>异步执行</summary>
        /// <param name="sql">语句</param>
        /// <param name="ps">参数集合</param>
        /// <returns></returns>
        public async Task<Int64> ExecuteAsync(String sql, IDictionary<String, Object> ps = null)
        {
            var arg = Encode(sql, ps);

            return await InvokeAsync<Int64>("Db/Execute", arg).ConfigureAwait(false);
        }
        #endregion

        #region 辅助
        private Packet Encode(String sql, IDictionary<String, Object> ps)
        {
            // 头部预留8字节,方便加协议头
            var bn = new Binary { EncodeInt = true };
            bn.Stream.Seek(8, SeekOrigin.Current);

            bn.Write(sql);

            if (ps != null && ps.Count > 0)
            {
                bn.Write(ps.Count);
                foreach (var item in ps)
                {
                    bn.Write(item.Key);

                    var tc = item.Value.GetType().GetTypeCode();
                    if (tc == TypeCode.Object) throw new NotSupportedException($"数据参数不支持类型{item.Value.GetType().FullName}");

                    bn.Write((Byte)tc);
                    bn.Write(item.Value);
                }
            }

            var ms = bn.Stream;
            ms.Position = 8;

            return new Packet(ms);
        }
        #endregion
    }
}