Example #1
0
        public void SetAsyncTest()
        {
            RedisManager.ConfigurationOption = "localhost:6379";
            CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);
            var cacheStrategy = CacheStrategyFactory.GetObjectCacheStrategyInstance();

            var dt = SystemTime.Now;

            cacheStrategy.Set("RedisTest", new ContainerBag()
            {
                Key     = "123",
                Name    = "",// Newtonsoft.Json.JsonConvert.SerializeObject(this),
                AddTime = dt
            });

            var obj = cacheStrategy.GetAsync <ContainerBag>("RedisTest").Result;

            Assert.IsNotNull(obj);
            Assert.IsInstanceOfType(obj, typeof(ContainerBag));
            //Console.WriteLine(obj);

            var containerBag = obj as ContainerBag;

            Assert.IsNotNull(containerBag);
            Assert.AreEqual(dt, containerBag.AddTime);

            Console.WriteLine($"SetTest单条测试耗时:{SystemTime.DiffTotalMS(dt)}ms");
        }
Example #2
0
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region 系统表

            //实现 [XncfAutoConfigurationMapping] 特性之后,可以自动执行,无需手动添加
            //modelBuilder.ApplyConfiguration(new AdminUserInfoConfigurationMapping());
            //modelBuilder.ApplyConfiguration(new FeedbackConfigurationMapping());

            #endregion

            #region 其他动态模块

            foreach (var databaseRegister in XncfRegisterManager.XncfDatabaseList)
            {
                Console.WriteLine("SenparcEntities 动态加载:" + databaseRegister.GetType().Name + " | DbContextType:" + databaseRegister.TryGetXncfDatabaseDbContextType.Name);
                databaseRegister.OnModelCreating(modelBuilder);
            }

            #endregion

            #region 全局自动注入(请勿改变此命令位置)

            //注册所有 XncfAutoConfigurationMapping 动态模块
            var dt1 = SystemTime.Now;
            Senparc.Ncf.XncfBase.Register.ApplyAllAutoConfigurationMapping(modelBuilder);
            SenparcTrace.SendCustomLog("SenparcEntities 数据库实体注入", $"耗时:{SystemTime.DiffTotalMS(dt1)}ms");
            #endregion

            //基类中的系统表处理
            base.OnModelCreating(modelBuilder);
        }
Example #3
0
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region 系统表

            //实现 [XncfAutoConfigurationMapping] 特性之后,可以自动执行,无需手动添加
            //modelBuilder.ApplyConfiguration(new AdminUserInfoConfigurationMapping());
            //modelBuilder.ApplyConfiguration(new FeedbackConfigurationMapping());

            #endregion

            #region 其他动态模块

            foreach (var databaseRegister in Senparc.Ncf.XncfBase.Register.XncfDatabaseList)
            {
                databaseRegister.OnModelCreating(modelBuilder);
            }

            #endregion

            #region 全局自动注入(请勿改变此命令位置)

            //注册所有 XncfAutoConfigurationMapping 动态模块
            var dt1 = SystemTime.Now;
            Senparc.Ncf.XncfBase.Register.ApplyAllAutoConfigurationMapping(modelBuilder);
            SenparcTrace.SendCustomLog("SenparcEntities 数据库实体注入", $"耗时:{SystemTime.DiffTotalMS(dt1)}ms");

            #endregion

            base.OnModelCreating(modelBuilder);
        }
Example #4
0
        public void RunTest()
        {
            var function = new ML.Xscf.Docs.Functions.UpdateDocs(base.ServiceProvider);
            var result   = function.Run(new UpdateDocs.UpdateDocs_Parameters());

            Console.WriteLine(result.Log);
            Console.WriteLine("===============");
            Console.WriteLine(result.Message);

            var filePath = Path.Combine(Senparc.CO2NET.Config.RootDictionaryPath, "wwwroot", "ScfDocs", "README.md");

            Assert.IsTrue(File.Exists(filePath));//断言文件存在

            var fetchFilePath = Path.Combine(Senparc.CO2NET.Config.RootDictionaryPath, "wwwroot", "ScfDocs", ".git", "FETCH_HEAD");

            if (File.Exists(fetchFilePath))
            {
                //更新项目
                Assert.IsTrue(SystemTime.DiffTotalMS(File.GetLastWriteTime(fetchFilePath)) < 100);//断言文件已被更新
            }
            else
            {
                //第一次拉取
            }
        }
        public void LocalCacheLockTest()
        {
            var resourceName = "TestLocalCacheLock";
            var key          = "test";
            int retryCount   = 10;
            var retryDelay   = TimeSpan.FromMilliseconds(20);

            using (var localCacheLock = LocalCacheLock.CreateAndLock(LocalObjectCacheStrategy.Instance, resourceName, key, retryCount, retryDelay).Lock())
            {
                //注意:常规情况下这里不能使用相同的 resourceName + key 组合,否则会造成死锁!!

                var dt0 = SystemTime.Now;
                Console.WriteLine($"锁定开始:{dt0}");
                while (SystemTime.DiffTotalMS(dt0) < retryCount * retryDelay.TotalMilliseconds + 1000)
                {
                    //确保足够的过期时间
                }

                Console.WriteLine($"localCacheLock.LockSuccessful:{localCacheLock.LockSuccessful}");

                using (var localCacheLock2 = LocalCacheLock.CreateAndLock(LocalObjectCacheStrategy.Instance, resourceName, key, retryCount, retryDelay).Lock())
                {
                    Console.WriteLine($"localCacheLock2.LockSuccessful:{localCacheLock2.LockSuccessful}");
                }
            }
        }
Example #6
0
        public void EfficiencyTest()
        {
            var dt1 = SystemTime.Now;

            for (int i = 0; i < 100; i++)
            {
                SetTest();
            }

            Console.WriteLine($"EfficiencyTest总测试时间(使用CacheWrapper):{SystemTime.DiffTotalMS(dt1)}ms");
        }
Example #7
0
        public void MessagePackTest()
        {
            //    CompositeResolver.RegisterAndSetAsDefault(
            //new[] { TypelessFormatter.Instance },
            //new[] { NativeDateTimeResolver.Instance, ContractlessStandardResolver.Instance });

            //            CompositeResolver.RegisterAndSetAsDefault(
            //    // Resolve DateTime first
            //    MessagePack.Resolvers.NativeDateTimeResolver.Instance,
            //    MessagePack.Resolvers.StandardResolver.Instance,
            //       MessagePack.Resolvers.BuiltinResolver.Instance,
            //                // use PrimitiveObjectResolver
            //                PrimitiveObjectResolver.Instance
            //);

            Console.WriteLine("开始异步测试");
            var threadCount = 10;
            var finishCount = 0;

            for (int i = 0; i < threadCount; i++)
            {
                var thread = new Thread(() =>
                {
                    var newObj = new ContainerBag()
                    {
                        Key     = Guid.NewGuid().ToString(),
                        Name    = Newtonsoft.Json.JsonConvert.SerializeObject(this),
                        AddTime = SystemTime.Now.ToUniversalTime()
                    };

                    var dtx           = SystemTime.Now;
                    var serializedObj = MessagePackSerializer.Serialize(newObj /*, NativeDateTimeResolver.Instance*/);
                    Console.WriteLine($"MessagePackSerializer.Serialize 耗时:{SystemTime.DiffTotalMS(dtx)}ms");

                    dtx = SystemTime.Now;
                    var containerBag = MessagePackSerializer.Deserialize <ContainerBag>(serializedObj);//11ms
                    Console.WriteLine($"MessagePackSerializer.Deserialize 耗时:{SystemTime.DiffTotalMS(dtx)}ms");

                    Console.WriteLine(containerBag.AddTime.ToUniversalTime());

                    //Assert.AreEqual(containerBag.AddTime.Ticks, newObj.AddTime.Ticks);
                    Assert.AreNotEqual(containerBag.GetHashCode(), newObj.GetHashCode());
                    finishCount++;
                });
                thread.Start();
            }

            while (finishCount < threadCount)
            {
                //等待
            }
        }
        public void BuildProviderTest()
        {
            for (int i = 0; i < 10; i++)
            {
                var services = new ServiceCollection();
                services.AddSingleton <Senparc.CO2NET.SenparcSetting>();

                var b1 = services.BuildServiceProvider();
                var b2 = services.BuildServiceProvider();
                var s1 = b1.GetRequiredService <SenparcSetting>();
                var s2 = b1.GetRequiredService <SenparcSetting>();
                Console.WriteLine($"s1-b1:{s1.GetHashCode()}");
                Console.WriteLine($"s2-b1:{s2.GetHashCode()}");

                s2 = b2.GetRequiredService <SenparcSetting>();
                Console.WriteLine($"s2-b2:{s2.GetHashCode()}");

                services.AddScoped <Senparc.CO2NET.Helpers.EncryptHelper>();
                services.AddScoped <Senparc.CO2NET.MessageQueue.MessageQueueDictionary>();
                services.AddScoped <Senparc.CO2NET.Utilities.ServerUtility>();
                //每次进行一些变化
                if (i % 2 == 0)
                {
                    services.AddScoped <Senparc.CO2NET.Utilities.BrowserUtility>();
                    services.AddScoped <Senparc.CO2NET.Helpers.DateTimeHelper>();
                    services.AddScoped <Senparc.CO2NET.Helpers.FileHelper>();
                }

                var dt1      = SystemTime.Now;
                var provider = services.BuildServiceProvider();
                Console.WriteLine($"HashCode:{provider.GetHashCode()},Time:{SystemTime.DiffTotalMS(dt1)}ms");


                Console.WriteLine("进行Scope测试");
                for (int j = 0; j < 2; j++)
                {
                    using (var scope = b1.CreateScope())
                    {
                        Console.WriteLine($"{j}.\tBuildServiceProvider:{b1.GetHashCode()}");
                        Console.WriteLine($"{j}.\tscope.ServiceProvider:{scope.ServiceProvider.GetHashCode()}");
                        //测试结果:两个 ServiceProvider 不是同一个对象


                        var scopeS1 = scope.ServiceProvider.GetRequiredService <SenparcSetting>();
                        Console.WriteLine($"{j}.\tscopeS1:{scope.ServiceProvider.GetHashCode()}");
                    }
                }
            }
        }
Example #9
0
        public void SendCustomLogTest()
        {
            var keyword = Guid.NewGuid().ToString();//随机字符串

            SenparcTrace.SendCustomLog("标题", $"添加Log:{keyword}");

            var dt1 = SystemTime.Now;

            while (SystemTime.DiffTotalMS(dt1) < 800)
            {
                //等待队列执行
            }

            Assert.IsTrue(UnitTestHelper.CheckKeywordsExist(LogFilePath, "标题", keyword));
        }
Example #10
0
        public void GetJsonStringTest_Expando()
        {
            dynamic test = new ExpandoObject();

            test.x = "Senparc.Weixin SDK";
            test.y = SystemTime.Now;

            var dt1 = SystemTime.Now;

            var json = SerializerHelper.GetJsonString(test);

            Console.WriteLine(json);

            Console.WriteLine(SystemTime.DiffTotalMS(dt1));
        }
Example #11
0
        public void SendApiLogTest()
        {
            var url    = "http://www.senparc.com";
            var result = Guid.NewGuid().ToString();//随机字符串

            SenparcTrace.SendApiLog(url, result);

            var dt1 = SystemTime.Now;

            while (SystemTime.DiffTotalMS(dt1) < 800)
            {
                //等待队列执行
            }

            Assert.IsTrue(UnitTestHelper.CheckKeywordsExist(LogFilePath, url, result));
        }
Example #12
0
        /// <summary>
        /// 测试 Parallel 和 Thread 的并行数量
        /// </summary>
        //[TestMethod]
        public void TestParallelThreadsRunAtSameTime()
        {
            var dt1 = SystemTime.Now;

            Parallel.For(0, 100, i =>
            {
                Console.WriteLine("T:{0},Use Time:{1}ms", Thread.CurrentThread.GetHashCode(),
                                  SystemTime.DiffTotalMS(dt1));
                Thread.Sleep(20);
            });

            var dt2 = SystemTime.Now;

            Console.WriteLine("Working Threads Count:{0}", 100 * 20 / (dt2 - dt1).TotalMilliseconds);
            //测试结果:同时运行的线程数约为4(平均3.6),实际目测为5

            Console.WriteLine("测试Threads");

            List <Thread> list = new List <Thread>();

            int[] runThreads = { 0 };

            for (int i = 0; i < 100; i++)
            {
                runThreads[0]++;
                var thread = new Thread(() =>
                {
                    Console.WriteLine("T:{0},Use Time:{1}ms", Thread.CurrentThread.GetHashCode(),
                                      SystemTime.DiffTotalMS(dt2));
                    Thread.Sleep(20);
                    runThreads[0]--;
                });
                list.Add(thread);
            }

            var dt3 = SystemTime.Now;

            list.ForEach(z => z.Start());
            while (runThreads[0] > 0)
            {
                //Thread.Sleep(100);
            }
            var dt4 = SystemTime.Now;

            Console.WriteLine("Working Threads Count:{0}", 1000 * 20 / (dt4 - dt3).TotalMilliseconds);
            //测试结果:无限制
        }
Example #13
0
        public void BaseExceptionLogTest()
        {
            var keyword = Guid.NewGuid().ToString();//随机字符串
            var ex      = new BaseException("测试异常:" + keyword);

            //Log会记录两次,第一次是在BaseException初始化的时候会调用此方法
            SenparcTrace.BaseExceptionLog(ex);

            var dt1 = SystemTime.Now;

            while (SystemTime.DiffTotalMS(dt1) < 800)
            {
                //等待队列执行
            }

            Assert.IsTrue(UnitTestHelper.CheckKeywordsExist(LogFilePath, "测试异常", keyword));
        }
Example #14
0
        public void CacheWrapperEfficiencyTest()
        {
            Console.WriteLine("开始CacheWrapper异步测试");
            var           threadCount = 10;
            var           finishCount = 0;
            List <Thread> threadList  = new List <Thread>();

            for (int i = 0; i < threadCount; i++)
            {
                var thread = new Thread(() =>
                {
                    var testClass = new TestClass()
                    {
                        ID      = Guid.NewGuid().ToString(),
                        Star    = SystemTime.Now.Ticks,
                        AddTime = SystemTime.Now
                    };

                    var dtx  = SystemTime.Now;
                    var json = testClass.SerializeToCache();
                    //Console.WriteLine(json);
                    Console.WriteLine($"testClass.SerializeToCache 耗时:{SystemTime.DiffTotalMS(dtx)}ms");


                    dtx     = SystemTime.Now;
                    var obj = json.DeserializeFromCache <TestClass>();
                    Console.WriteLine($"json.DeserializeFromCache<TestClass> 耗时:{SystemTime.DiffTotalMS(dtx)}ms");
                    Assert.AreEqual(obj.ID, testClass.ID);
                    Assert.AreEqual(obj.Star, testClass.Star);
                    Assert.AreEqual(obj.AddTime, testClass.AddTime);

                    Console.WriteLine("");

                    finishCount++;
                });
                threadList.Add(thread);
            }

            threadList.ForEach(z => z.Start());

            while (finishCount < threadCount)
            {
                //等待
            }
        }
Example #15
0
        public void GetDocMethodNameTest()
        {
            var webApiEngine = new WebApiEngine(null);

            {
                var input  = new XAttribute("name", "M:Senparc.Weixin.MP.AdvancedAPIs.AnalysisApi.GetArticleSummary(System.String,System.String,System.String,System.Int32)");
                var result = webApiEngine.GetDocMethodInfo(input);
                Assert.AreEqual("Senparc.Weixin.MP.AdvancedAPIs.AnalysisApi.GetArticleSummary", result.MethodName);
                Assert.AreEqual("(System.String,System.String,System.String,System.Int32)", result.ParamsPart);
            }
            {
                var input  = new XAttribute("name", "T:Senparc.Weixin.MP.AdvancedAPIs.AnalysisApi");
                var result = webApiEngine.GetDocMethodInfo(input);
                Assert.AreEqual(null, result.MethodName);
                Assert.AreEqual(null, result.ParamsPart);
            }
            {
                var input  = new XAttribute("name", "P:Senparc.Weixin.MP.AdvancedAPIs.ShakeAround.QueryLottery_Result.drawed_value");
                var result = webApiEngine.GetDocMethodInfo(input);
                Assert.AreEqual(null, result.MethodName);
                Assert.AreEqual(null, result.ParamsPart);
            }
            {
                var input  = new XAttribute("name", "F:Senparc.Weixin.MP.MemberCard_CustomField_NameType.FIELD_NAME_TYPE_UNKNOW");
                var result = webApiEngine.GetDocMethodInfo(input);
                Assert.AreEqual(null, result.MethodName);
                Assert.AreEqual(null, result.ParamsPart);
            }

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine();
                {
                    var dt1    = SystemTime.Now;
                    var input  = new XAttribute("name", "M:Senparc.Weixin.MP.AdvancedAPIs.TemplateApi.SendTemplateMessage(System.String,System.String,Senparc.Weixin.Entities.TemplateMessage.ITemplateMessageBase,Senparc.Weixin.MP.AdvancedAPIs.TemplateMessage.TempleteModel_MiniProgram,System.Int32)");
                    var result = webApiEngine.GetDocMethodInfo(input);
                    Console.WriteLine("Method 1 Cost:" + SystemTime.DiffTotalMS(dt1) + "ms");

                    Assert.AreEqual("Senparc.Weixin.MP.AdvancedAPIs.TemplateApi.SendTemplateMessage", result.MethodName);
                    Assert.AreEqual("(System.String,System.String,Senparc.Weixin.Entities.TemplateMessage.ITemplateMessageBase,Senparc.Weixin.MP.AdvancedAPIs.TemplateMessage.TempleteModel_MiniProgram,System.Int32)", result.ParamsPart);
                }
            }
        }
Example #16
0
        public void OnLogFuncTest()
        {
            var onlogCount = 0;

            SenparcTrace.OnLogFunc = () => onlogCount++;

            var keyword = Guid.NewGuid().ToString();//随机字符串

            SenparcTrace.SendCustomLog("测试OnLogFuncTest", keyword);

            var dt1 = SystemTime.Now;

            while (SystemTime.DiffTotalMS(dt1) < 800)
            {
                //等待队列执行
            }

            Assert.IsTrue(UnitTestHelper.CheckKeywordsExist(LogFilePath, keyword));
            Assert.AreEqual(1, onlogCount);
        }
Example #17
0
        public void LogTest()
        {
            //直接调用此方法不会记录到log文件中,而是输出到系统日志中
            var keyword = Guid.NewGuid().ToString();//随机字符串

            SenparcTrace.Log($"添加Log:{keyword}");

            var dt1 = SystemTime.Now;

            while (SystemTime.DiffTotalMS(dt1) < 600)
            {
                //等待队列执行
            }

            SenparcMessageQueue.OperateQueue();

            Console.WriteLine(SenparcMessageQueue.MessageQueueDictionary.Count);

            Console.WriteLine(ThreadUtility.AsynThreadCollection.Count);
            //Assert.IsTrue(UnitTestHelper.CheckKeywordsExist(_logFilePath,keyword));
        }
Example #18
0
        public void OnBaseExceptionFuncTest()
        {
            var onlogCount = 0;

            SenparcTrace.OnLogFunc = () => onlogCount++;

            var keyword = Guid.NewGuid().ToString();//随机字符串
            var ex      = new BaseException("测试异常:" + keyword);

            //Log会记录两次,第一次是在BaseException初始化的时候会调用此方法
            SenparcTrace.BaseExceptionLog(ex);

            var dt1 = SystemTime.Now;

            while (SystemTime.DiffTotalMS(dt1) < 800)
            {
                //等待队列执行
            }

            Assert.IsTrue(UnitTestHelper.CheckKeywordsExist(LogFilePath, keyword));
            Assert.AreEqual(2, onlogCount);
        }
Example #19
0
        /// <summary>
        /// 设置数据
        /// </summary>
        /// <param name="kindName">统计类别名称</param>
        /// <param name="value">统计值</param>
        /// <param name="data">复杂类型数据</param>
        /// <param name="tempStorage">临时储存信息</param>
        /// <param name="dateTime">发生时间,默认为当前系统时间</param>
        /// <returns></returns>
        public async Task <DataItem> SetAsync(string kindName, double value, object data = null, object tempStorage = null, DateTimeOffset?dateTime = null)
        {
            if (!Config.EnableAPM)
            {
                return(null);//不启用,不进行记录
            }

            try
            {
                var dt1           = SystemTime.Now;
                var cacheStragety = Cache.CacheStrategyFactory.GetObjectCacheStrategyInstance();
                var finalKey      = BuildFinalKey(kindName);
                //使用同步锁确定写入顺序
                using (await cacheStragety.BeginCacheLockAsync("SenparcAPM", finalKey).ConfigureAwait(false))
                {
                    var dataItem = new DataItem()
                    {
                        KindName    = kindName,
                        Value       = value,
                        Data        = data,
                        TempStorage = tempStorage,
                        DateTime    = dateTime ?? SystemTime.Now
                    };

                    var list = await GetDataItemListAsync(kindName).ConfigureAwait(false);

                    list.Add(dataItem);
                    await cacheStragety.SetAsync(finalKey, list, Config.DataExpire, true).ConfigureAwait(false);

                    await RegisterFinalKeyAsync(kindName).ConfigureAwait(false);//注册Key

                    if (SenparcTrace.RecordAPMLog)
                    {
                        SenparcTrace.SendCustomLog($"APM 性能记录 - DataOperation.Set - {_domain}:{kindName}", SystemTime.DiffTotalMS(dt1) + " ms");
                    }

                    return(dataItem);
                }
            }
            catch (Exception e)
            {
                new APMException(e.Message, _domain, kindName, $"DataOperation.Set -  {_domain}:{kindName}");
                return(null);
            }
        }
Example #20
0
        /// <summary>
        /// 开始 Senparc.Weixin SDK 初始化参数流程
        /// </summary>
        /// <param name="registerService"></param>
        /// <param name="senparcWeixinSetting">微信全局设置参数,必填</param>
        /// <param name="senparcSetting">用于提供 SenparcSetting.Cache_Redis_Configuration 和 Cache_Memcached_Configuration 两个参数,如果不使用这两种分布式缓存可传入null</param>
        /// <returns></returns>
        public static IRegisterService UseSenparcWeixin(this IRegisterService registerService, SenparcWeixinSetting senparcWeixinSetting, SenparcSetting senparcSetting = null)
        {
            senparcWeixinSetting = senparcWeixinSetting ?? new SenparcWeixinSetting();
            senparcSetting       = (senparcSetting ?? CO2NET.Config.SenparcSetting) ?? new SenparcSetting();

            //Senparc.Weixin SDK 配置
            Senparc.Weixin.Config.SenparcWeixinSetting = senparcWeixinSetting;

            /* 扩展缓存注册开始 */
            var cacheTypes = "";//所有注册的扩展缓存

            // 微信的 本地 缓存
            var cache = LocalContainerCacheStrategy.Instance;//只要引用就可以被激活

            cacheTypes += typeof(LocalContainerCacheStrategy);

            var dt1 = SystemTime.Now;

            //官方扩展缓存注册

            //var officialTypes = new List<Type>() { typeof(LocalContainerCacheStrategy) };//官方提供的扩展缓存策略

            //自动注册 Redis 和 Memcached
            //Redis
            var redisConfiguration = senparcSetting.Cache_Redis_Configuration;

            if (!string.IsNullOrEmpty(redisConfiguration) &&
                /*缓存配置默认值,不启用*/
                redisConfiguration != "Redis配置" &&
                redisConfiguration != "#{Cache_Redis_Configuration}#")
            {
                try
                {
                    {
                        //StackExchange.Redis
                        var redisInstance = ReflectionHelper.GetStaticMember("Senparc.Weixin.Cache.Redis", "Senparc.Weixin.Cache.Redis", "RedisContainerCacheStrategy", "Instance");
                        if (redisInstance != null)
                        {
                            //officialTypes.Add(redisInstance.GetType());
                            cacheTypes += "\r\n" + redisInstance.GetType();
                        }
                    }
                    {
                        //CsRedis
                        var redisInstance = ReflectionHelper.GetStaticMember("Senparc.Weixin.Cache.CsRedis", "Senparc.Weixin.Cache.CsRedis", "RedisContainerCacheStrategy", "Instance");
                        if (redisInstance != null)
                        {
                            //officialTypes.Add(redisInstance.GetType());
                            cacheTypes += "\r\n" + redisInstance.GetType();
                        }
                    }
                }
                catch (Exception ex)
                {
                    WeixinTrace.WeixinExceptionLog(new Exceptions.WeixinException(ex.Message, ex));
                }
            }

            //Memcached
            var memcachedConfiguration = senparcSetting.Cache_Memcached_Configuration;

            if (!string.IsNullOrEmpty(memcachedConfiguration) &&
                /*缓存配置默认值,不启用*/
                memcachedConfiguration != "Memcached配置" &&
                memcachedConfiguration != "#{Cache_Memcached_Configuration}#")
            {
                try
                {
                    var memcachedInstance = ReflectionHelper.GetStaticMember("Senparc.Weixin.Cache.Memcached", "Senparc.Weixin.Cache.Memcached", "MemcachedContainerCacheStrategy", "Instance");
                    if (memcachedInstance != null)
                    {
                        //officialTypes.Add(memcachedInstance.GetType());
                        cacheTypes += "\r\n" + memcachedInstance.GetType();
                    }
                }
                catch (Exception ex)
                {
                    WeixinTrace.WeixinExceptionLog(new Exceptions.WeixinException(ex.Message, ex));
                }
            }

            var exCacheLog = $"微信扩展缓存注册总用时:{SystemTime.DiffTotalMS(dt1, "f4")}ms\r\n扩展缓存:{cacheTypes}";

            WeixinTrace.SendCustomLog("微信扩展缓存注册完成", exCacheLog);

            /* 扩展缓存注册结束 */

            //ApiBind 自动扫描
            Senparc.NeuChar.Register.RegisterApiBind(false);

            return(registerService);
        }
        public ActionResult Index(string url, string token, RequestMsgType requestType, Event?eventType,
                                  bool testConcurrence, int testConcurrenceCount,
                                  bool testEncrypt, string encodingAESKey, string appId)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                var requestMessaageDoc = GetrequestMessaageDoc(/*url, token,*/ requestType, eventType);
                requestMessaageDoc.Save(ms);
                ms.Seek(0, SeekOrigin.Begin);

                string msgSigature    = null;
                var    timeStamp      = SystemTime.NowTicks.ToString();
                var    nonce          = (SystemTime.NowTicks * 2).ToString();
                string encryptTypeAll = null;
                string openIdAll      = null;

                //对请求消息进行加密
                if (testEncrypt)
                {
                    try
                    {
                        var openId     = requestMessaageDoc.Root.Element("FromUserName").Value;
                        var toUserName = requestMessaageDoc.Root.Element("ToUserName").Value;

                        WXBizMsgCrypt msgCrype         = new WXBizMsgCrypt(token, encodingAESKey, appId);
                        string        finalResponseXml = null;
                        var           ret = msgCrype.EncryptRequestMsg(requestMessaageDoc.ToString(), timeStamp, nonce, toUserName, ref finalResponseXml, ref msgSigature);

                        if (ret == 0)
                        {
                            requestMessaageDoc = XDocument.Parse(finalResponseXml);//赋值最新的加密信息
                            openIdAll          = $"openid={openId}";
                            encryptTypeAll     = "&encrypt_type=aes";
                        }
                    }
                    catch (Exception ex)
                    {
                        var data = new { Success = false, LoadTime = "N/A", Result = "发生错误:" + ex.ToString() };
                        return(Json(data, new JsonSerializerSettings()
                        {
                            ContractResolver = new DefaultContractResolver()
                        }));

                        throw;
                    }

                    //Senparc.CO2NET.Trace.SenparcTrace.SendCustomLog("模拟测试-加密消息:", requestMessaageDoc?.ToString());
                }

                var sigature = CheckSignature.GetSignature(timeStamp, nonce, token);

                url += url.Contains("?") ? "&" : "?";
                url += $"signature={sigature}&timeStamp={timeStamp}&nonce={nonce}&msg_signature={msgSigature}{encryptTypeAll}{openIdAll}";
                //参数如:signature=330ed3b64e363dc876f35e54a79e59b48739f567&timestamp=1570075722&nonce=863153744&openid=olPjZjsXuQPJoV0HlruZkNzKc91E&encrypt_type=aes&msg_signature=71dc359205a4660bc3b3046b643452c994b5897d

                var dt1 = SystemTime.Now;


                try
                {
                    dt1 = SystemTime.Now;
                    if (testConcurrence)
                    {
                        //异步方法
                        testConcurrenceCount = testConcurrenceCount > 30 ? 30 : testConcurrenceCount;//设定最高限额

                        //模拟并发请求
                        List <Task <string> > taskList = new List <Task <string> >();
                        for (int i = 0; i < testConcurrenceCount; i++)
                        {
                            var task = TestAsyncTask(url, token, requestMessaageDoc, autoFillUrlParameters: false, index: i, sleepMillionSeconds: 0);
                            taskList.Add(task);
                        }
                        Task.WaitAll(taskList.ToArray(), 1500 * 10);
                    }
                    else
                    {
                        //同步方法,立即发送
                        _responseMessageXml = MessageAgent.RequestXml(null, url, token, requestMessaageDoc.ToString(), autoFillUrlParameters: false);
                    }


                    if (string.IsNullOrEmpty(_responseMessageXml))
                    {
                        _responseMessageXml = "返回消息为空,可能已经被去重。\r\nMsgId相同的连续消息将被自动去重。";
                    }

                    var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance();
                    var data  = new
                    {
                        Success          = true,
                        LoadTime         = SystemTime.DiffTotalMS(dt1, "f4"),
                        Result           = _responseMessageXml,
                        CacheType        = cache.GetType().Name,
                        ConcurrenceCount = testConcurrenceCount
                    };
                    return(Json(data, new JsonSerializerSettings()
                    {
                        ContractResolver = new DefaultContractResolver()
                    }));
                }
                catch (Exception ex)
                {
                    var msg = string.Format("{0}\r\n{1}\r\n{2}", ex.Message, null, ex.InnerException != null ? ex.InnerException.Message : null);
                    return(Json(new { Success = false, Result = msg }, new JsonSerializerSettings()
                    {
                        ContractResolver = new DefaultContractResolver()
                    }));
                }
            }
        }
Example #22
0
        public void LockTest()
        {
            var cache = CacheStrategyFactory.GetObjectCacheStrategyInstance();

            Console.WriteLine("Redis 配置:" + BaseTest._senparcSetting.Cache_Redis_Configuration);
            Console.WriteLine("当前缓存策略:" + cache.GetType());

            var lockResourceName = "LockTest";

            var threadCount       = 60;                                                 //定义线程数量
            var runningCount      = 0;
            var finishThreadCount = 0;                                                  //已完成线程数量
            var retryCount        = 10;                                                 //重试次数(Redis 默认10)
            var retryDelay        = TimeSpan.FromMilliseconds(100);                     //单次重试等待时间(Redis 默认10毫秒)
            Dictionary <int, Thread> threadCollection = new Dictionary <int, Thread>(); //线程集合

            var dtStart  = SystemTime.Now;
            var startRun = false;

            //配置所有线程(为了尽量同时开始,忽略设置时间,所以先设置,再依次快速开启)
            for (int i = 0; i < threadCount; i++)
            {
                var lockGroupKey = i % 4;//分为4组
                var threadIndex  = i;
                threadCollection[i] = new Thread(async() =>
                {
                    runningCount++;
                    //while (runningCount < threadCount)
                    {
                        //等待统一开始
                    }
                    var dt1 = SystemTime.Now;
                    using (var syncLock = cache.BeginCacheLock(lockResourceName, lockGroupKey.ToString(), retryCount, retryDelay))
                    {
                        var runTime  = SystemTime.Now;
                        var waitTime = (runTime - dt1).TotalMilliseconds;
                        Thread.Sleep(100);//模拟线程处理时间
                        finishThreadCount++;
                        Console.WriteLine($"{runTime.ToString("ss.ffffff")} - {SystemTime.Now.ToString("ss.ffffff")}\t\t 等待:{waitTime}ms \t\t获取syncLock完成(Group:{lockGroupKey}):{threadIndex} - {syncLock.LockSuccessful}");
                    }
                });
                threadCollection[i].Name = $"Lock-{i}";
            }

            //开始所有线程

            //此方法会导致死循环
            //threadCollection.Values.ToList().AsParallel().ForAll(thread => thread.Start());

            foreach (var thread in threadCollection.Values)
            {
                thread.Start();//注意:线程实际启动完成时间不一定是按序的
            }

            while (finishThreadCount < threadCount)
            {
                //等待完成
            }

            Console.WriteLine($"过程结束,总用时:{SystemTime.DiffTotalMS(dtStart)} ms");
        }
Example #23
0
        public void GetJsonStringTest_Null()
        {
            var obj =
                new
            {
                X =
                    new RootClass()
                {
                    A             = "Jeffrey",
                    B             = 31,
                    C             = null,
                    ElementClassA = new ElementClass()
                    {
                        A = "Jeffrey", B = null
                    },
                    ElementClassB = null
                },
                Y = new
                {
                    O = "0",
                    Z = (string)null
                }
            };


            var dt1 = SystemTime.Now;

            {
                //不进行任何设置,返回原始JSON
                var json = SerializerHelper.GetJsonString(obj, jsonSetting: null);
                Console.WriteLine(json);
                var exceptedJson = "{\"X\":{\"A\":\"Jeffrey\",\"B\":31,\"C\":null,\"ElementClassA\":{\"A\":\"Jeffrey\",\"B\":null,\"RootClass\":null},\"ElementClassB\":null,\"ElementClass2\":null},\"Y\":{\"O\":\"0\",\"Z\":null}}";
                Assert.AreEqual(exceptedJson, json);
            }


            {
                //不忽略任何属性
                var json = SerializerHelper.GetJsonString(obj, new JsonSetting(false));
                Console.WriteLine(json);
                var exceptedJson = "{\"X\":{\"A\":\"Jeffrey\",\"B\":31,\"ElementClassA\":{\"A\":\"Jeffrey\",\"B\":null,\"RootClass\":null},\"ElementClassB\":null,\"ElementClass2\":null},\"Y\":{\"O\":\"0\",\"Z\":null}}";
                Assert.AreEqual(exceptedJson, json);
            }

            {
                //忽略所有为null的属性
                var json = SerializerHelper.GetJsonString(obj, new JsonSetting(true));
                Console.WriteLine(json);
                var exceptedJson = "{\"X\":{\"A\":\"Jeffrey\",\"B\":31,\"ElementClassA\":{\"A\":\"Jeffrey\"}},\"Y\":{\"O\":\"0\"}}";
                Assert.AreEqual(exceptedJson, json);


                var obj2 = new RootClass()
                {
                    A             = "Jeffrey",
                    B             = 31,
                    C             = null,
                    ElementClassA = new ElementClass()
                    {
                        A = "Jeffrey", B = null
                    },
                    ElementClassB = null
                };

                var json2 = SerializerHelper.GetJsonString(obj2,
                                                           new JsonSetting(true, new List <string>(new[] { "B" })));

                var exceptedJson2 = "{\"A\":\"Jeffrey\",\"B\":31,\"ElementClassA\":{\"A\":\"Jeffrey\"}}";
                Console.WriteLine(json2);
                Assert.AreEqual(exceptedJson2, json2);
            }

            {
                //忽略特定为null的属性
                var json = SerializerHelper.GetJsonString(obj,
                                                          new JsonSetting(false, new List <string>(new[] { "Z" })));//Z属性会被忽略
                Console.WriteLine(json);
                var exceptedJson = "{\"X\":{\"A\":\"Jeffrey\",\"B\":31,\"ElementClassA\":{\"A\":\"Jeffrey\",\"B\":null,\"RootClass\":null},\"ElementClassB\":null,\"ElementClass2\":null},\"Y\":{\"O\":\"0\"}}";
                Assert.AreEqual(exceptedJson, json);
            }

            {
                //忽略特定值测试(忽略特定值,以及忽略null)
                var obj4 = new RootClass()
                {
                    A             = "IGNORE",//会被忽略
                    B             = 31,
                    C             = null,
                    ElementClassA = null,
                    ElementClassB = null,
                    ElementClass2 = null
                };
                var json = SerializerHelper.GetJsonString(obj4, new JsonSetting(true));//Z属性会被忽略
                Console.WriteLine(json);
                var exceptedJson = "{\"B\":31}";
                Assert.AreEqual(exceptedJson, json);
            }

            {
                //忽略特定值测试(只忽略特定值,不忽略null)
                var obj4 = new RootClass()
                {
                    A             = "IGNORE",//会被忽略
                    B             = 31,
                    C             = null,
                    ElementClassA = null,
                    ElementClassB = null,
                    ElementClass2 = null
                };
                var json = SerializerHelper.GetJsonString(obj4, new JsonSetting(false));//Z属性会被忽略
                Console.WriteLine(json);
                var exceptedJson = "{\"B\":31,\"ElementClassA\":null,\"ElementClassB\":null,\"ElementClass2\":null}";
                Assert.AreEqual(exceptedJson, json);
            }

            {
                //忽略特定值测试(不匹配,因此不忽略)
                var obj4 = new RootClass()
                {
                    A             = "DO NET IGNORE",//不会被忽略
                    B             = 31,
                    C             = null,
                    ElementClassA = null,
                    ElementClassB = null,
                    ElementClass2 = null
                };
                var json = SerializerHelper.GetJsonString(obj4, new JsonSetting(true));//Z属性会被忽略
                Console.WriteLine(json);
                var exceptedJson = "{\"A\":\"DO NET IGNORE\",\"B\":31}";
                Assert.AreEqual(exceptedJson, json);
            }

            {
                //忽略特定类型为null的属性
                var obj3 = new RootClass()
                {
                    A             = "Jeffrey",
                    B             = 31,
                    C             = null,
                    ElementClassA = new ElementClass()
                    {
                        A = "Jeffrey", B = null
                    },
                    ElementClassB = null,
                    ElementClass2 = null//将会被忽略
                };

                var json3 = SerializerHelper.GetJsonString(obj3,
                                                           new JsonSetting(false, null, new List <Type>(new[] { typeof(ElementClass), typeof(ElementClass2) })));
                Console.WriteLine(json3);
                var exceptedJson3 = "{\"A\":\"Jeffrey\",\"B\":31,\"ElementClassA\":{\"A\":\"Jeffrey\",\"B\":null,\"RootClass\":null}}";
                Assert.AreEqual(exceptedJson3, json3);
            }



            Console.WriteLine(SystemTime.DiffTotalMS(dt1));
        }
Example #24
0
        public void StackExchangeRedisExtensionsTest()
        {
            Console.WriteLine("开始异步测试");
            var threadCount = 100;
            var finishCount = 0;

            for (int i = 0; i < threadCount; i++)
            {
                var thread = new Thread(() =>
                {
                    var newObj = new ContainerBag()
                    {
                        Key     = Guid.NewGuid().ToString(),
                        Name    = Newtonsoft.Json.JsonConvert.SerializeObject(this),
                        AddTime = SystemTime.Now
                    };
                    var dtx           = SystemTime.Now;
                    var serializedObj = CacheSerializeExtension.SerializeToCache(newObj);
                    Console.WriteLine($"StackExchangeRedisExtensions.Serialize耗时:{SystemTime.DiffTotalMS(dtx)}ms");

                    dtx = SystemTime.Now;
                    var containerBag = CacheSerializeExtension.DeserializeFromCache <ContainerBag>((string)serializedObj);//11ms
                    Console.WriteLine($"StackExchangeRedisExtensions.Deserialize耗时:{SystemTime.DiffTotalMS(dtx)}ms");

                    Assert.AreEqual(containerBag.AddTime.Ticks, newObj.AddTime.Ticks);
                    Assert.AreNotEqual(containerBag.GetHashCode(), newObj.GetHashCode());
                    finishCount++;
                });
                thread.Start();
            }

            while (finishCount < threadCount)
            {
                //等待
            }


            Action action = () =>
            {
                var newObj = new ContainerBag()
                {
                    Key     = Guid.NewGuid().ToString(),
                    Name    = Newtonsoft.Json.JsonConvert.SerializeObject(this),
                    AddTime = SystemTime.Now
                };
                var dtx           = SystemTime.Now;
                var serializedObj = CacheSerializeExtension.SerializeToCache(newObj);
                Console.WriteLine($"StackExchangeRedisExtensions.Serialize耗时:{SystemTime.DiffTotalMS(dtx)}ms");

                dtx = SystemTime.Now;
                var containerBag = CacheSerializeExtension.DeserializeFromCache <ContainerBag>((string)serializedObj);//11ms
                Console.WriteLine($"StackExchangeRedisExtensions.Deserialize耗时:{SystemTime.DiffTotalMS(dtx)}ms");

                Assert.AreEqual(containerBag.AddTime.Ticks, newObj.AddTime.Ticks);
                Assert.AreNotEqual(containerBag.GetHashCode(), newObj.GetHashCode());
            };

            Console.WriteLine("开始同步测试");
            for (int i = 0; i < 10; i++)
            {
                action();
            }
        }
Example #25
0
        private async Task <int> OnExecuteAsync(CommandLineApplication app, CancellationToken cancellationToken = default)
        {
            if (ShowResource == true)
            {
                Console.WriteLine(@"1、开源项目:https://github.com/JeffreySu/WeiXinMPSDK

2、微信技术交流社区:https://weixin.senparc.com/QA

3、chm帮助文档下载:https://sdk.weixin.senparc.com/Document

4、Senparc.Weixin SDK官网地址:https://weixin.senparc.com

5、Senparc.Weixin.MP.Sample Demo地址:https://sdk.weixin.senparc.com

6、微信开发资源收集:https://github.com/JeffreySu/WeixinResource");
                return(0);
            }


            if (QqGroup == true)
            {
                var dt1        = SystemTime.Now;
                var qqUrl      = "https://dev.senparc.com/WeixinSdk/GetSdkQqGroupJson";
                var resultTask = Senparc.CO2NET.HttpUtility.Get.GetJsonAsync <List <SdkQQGroup> >(_serviceProvider, qqUrl);

                while (!resultTask.IsCompleted)
                {
                    WriteColorLine(".", ConsoleColor.DarkGray, false);
                    await Task.Delay(50);
                }
                WriteColorLine($"{SystemTime.DiffTotalMS(dt1)}ms", ConsoleColor.DarkGray, false);
                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("QQ群(实时更新):");
                foreach (var item in resultTask.Result)
                {
                    WriteColorLine($"{item.GroupName}: {item.GroupNumber} ({item.Note}){(item.Open ? "开放申请" : "已满")}", item.Open ? ConsoleColor.Yellow : ConsoleColor.DarkGray);
                }
                Console.WriteLine();
                WriteColorLine($"友情提示:为开发者保留资源,请勿重复加群,谢谢合作!", ConsoleColor.DarkBlue, false);
                return(0);
            }


            if (string.IsNullOrEmpty(Keyword))
            {
                app.ShowHelp();
                return(0);
            }

            if (HostUrl != null)
            {
                _hostUrl = HostUrl.TrimEnd('/');
            }

            string platformQuery = null;
            string platformText  = null;
            List <PlatformType> platformNames = new List <PlatformType>();

            Platform?.ToList().ForEach(p =>
            {
                if (p == null)
                {
                    return;
                }
                platformQuery += $"&platformType={p}";
                try
                {
                    platformNames.Add((PlatformType)Enum.Parse(typeof(PlatformType), p));
                }
                catch (Exception ex)
                {
                }
            });
            if (platformNames.Count > 0)
            {
                platformText = string.Join(',', platformNames.Select(z => z.ToString()));
            }

            var apiUrl  = $"{_hostUrl}/FindWeixinAPi";
            var fullUrl = $"{apiUrl}?keyword={Keyword}{platformQuery}&isAsync={Async}";

            try
            {
                WriteColorLine($"搜索条件 >  关键字:{Keyword}    限定方法:{(Async.HasValue ? (Async.Value ? "异步" : "同步") : "全部")}    限定平台:{(platformText ?? "全部")}", ConsoleColor.Cyan);

                var dt1        = SystemTime.Now;
                var resultTask = Senparc.CO2NET.HttpUtility.Get.GetJsonAsync <FindWeixinApiResult>(_serviceProvider, fullUrl);

                while (!resultTask.IsCompleted)
                {
                    WriteColorLine(".", ConsoleColor.DarkGray, false);
                    await Task.Delay(50);
                }
                WriteColorLine($"{SystemTime.DiffTotalMS(dt1)}ms", ConsoleColor.DarkGray, false);
                var result = resultTask.Result;

                Console.WriteLine();

                var hasResult = result.ApiItemList.Count() > 0;
                for (int i = 0; i < result.ApiItemList.Count(); i++)
                {
                    var item = result.ApiItemList.ElementAt(i);
                    WriteColorLine($"[{i:0}] \t{GetFirstLineOfSummary(item.Summary)}", ConsoleColor.Green);
                    Console.WriteLine($"\t\t方法:{item.FullMethodName}");
                    //Console.WriteLine($"\t\t参数:{item.ParamsPart}");
                }

                Console.WriteLine();
                if (hasResult)
                {
                    WriteColorLine("输入编号并回车可直接展示对应代码示例,按回车(Enter)退出...", ConsoleColor.Yellow);

                    var indexStr = Console.ReadLine();
                    if (!indexStr.IsNullOrEmpty() && int.TryParse(indexStr, out int index))
                    {
                        if (index >= result.ApiItemList.Count())
                        {
                            Console.WriteLine("输入超出范围,已退出");
                        }
                        else
                        {
                            var apiItem = result.ApiItemList.ElementAt(index);
                            var code    = $"var result = {(apiItem.IsAsync == true ? "await " : "")}{apiItem.FullMethodName}();";
                            Console.WriteLine();
                            Console.WriteLine("请复制以下命令直接使用:");
                            Console.WriteLine();
                            WriteColorLine($"\t//调用 {GetFirstLineOfSummary(apiItem.Summary).Replace("【异步方法】", "")} 接口", ConsoleColor.Green);
                            WriteColorLine($"\t{code}", ConsoleColor.White);
                            Console.WriteLine();
                            WriteColorLine($"\t参数:{apiItem.ParamsPart}", ConsoleColor.DarkGray);
                            WriteColorLine($"\t完整注释:{apiItem.Summary.Trim()}", ConsoleColor.DarkGray);
                        }
                    }
                }
                else
                {
                    WriteColorLine("没有找到想要的接口?让社区一起来实现:https://github.com/JeffreySu/WeiXinMPSDK/issues/new", ConsoleColor.Yellow);
                    WriteColorLine("[Y]是    [N]否", ConsoleColor.Green);

                    var yesOrNo = Console.ReadLine();
                    if (yesOrNo.Equals("Y", StringComparison.OrdinalIgnoreCase))
                    {
                        //var url = "https://github.com/JeffreySu/WeiXinMPSDK/issues/new";
                        var url = "https://dev.senparc.com/QA";
                        OpenUrl(url);
                    }
                    else
                    {
                        WriteColorLine("欢迎前往开发者社区交流:https://dev.senparc.com/QA", ConsoleColor.Yellow);
                    }
                }
            }
            catch (HttpRequestException ex)
            {
                WriteColorLine("执行错误", ConsoleColor.Red);
                if (ex.Message.Contains("404 (Not Found)"))
                {
                    WriteColorLine($"\t请检查 HostUrl 设置是否正确,网站接口可能无法访问:{apiUrl}?keyword=anything", ConsoleColor.Red);
                }
                WriteColorLine($"\t{ex.Message}", ConsoleColor.Red);
            }
            return(1);
        }
Example #26
0
        static void Main(string[] args)
        {
            var dt1 = SystemTime.Now;

            var configBuilder = new ConfigurationBuilder();

            configBuilder.AddJsonFile("appsettings.json", false, false);
            Console.WriteLine("完成 appsettings.json 添加");

            var config = configBuilder.Build();

            Console.WriteLine("完成 ServiceCollection 和 ConfigurationBuilder 初始化");

            //更多绑定操作参见:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2
            var senparcSetting = new SenparcSetting();

            config.GetSection("SenparcSetting").Bind(senparcSetting);


            var services = new ServiceCollection();

            services.AddMemoryCache();//使用本地缓存必须添加

            /*
             * CO2NET 是从 Senparc.Weixin 分离的底层公共基础模块,经过了长达 6 年的迭代优化,稳定可靠。
             * 关于 CO2NET 在所有项目中的通用设置可参考 CO2NET 的 Sample:
             * https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore/Startup.cs
             */

            services.AddSenparcGlobalServices(config);//Senparc.CO2NET 全局注册
            Console.WriteLine("完成 AddSenparcGlobalServices 注册");

            // 启动 CO2NET 全局注册,必须!
            IRegisterService register = RegisterService.Start(senparcSetting)
                                        //关于 UseSenparcGlobal() 的更多用法见 CO2NET Demo:https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore/Startup.cs
                                        .UseSenparcGlobal();

            Console.WriteLine("完成 RegisterService.Start().UseSenparcGlobal()  启动设置");
            Console.WriteLine($"设定程序目录为:{Senparc.CO2NET.Config.RootDictionaryPath}");

            #region CO2NET 全局配置

            #region 全局缓存配置(按需)

            //当同一个分布式缓存同时服务于多个网站(应用程序池)时,可以使用命名空间将其隔离(非必须)
            register.ChangeDefaultCacheNamespace("DefaultCO2NETCache");
            Console.WriteLine($"默认缓存命名空间替换为:{CO2NET.Config.DefaultCacheNamespace}");


            #region 配置和使用 Redis          -- DPBMARK Redis

            //配置全局使用Redis缓存(按需,独立)
            var redisConfigurationStr = senparcSetting.Cache_Redis_Configuration;
            var useRedis = !string.IsNullOrEmpty(redisConfigurationStr) && redisConfigurationStr != "#{Cache_Redis_Configuration}#" /*默认值,不启用*/;
            if (useRedis)//这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略
            {
                /* 说明:
                 * 1、Redis 的连接字符串信息会从 Config.SenparcSetting.Cache_Redis_Configuration 自动获取并注册,如不需要修改,下方方法可以忽略
                 * /* 2、如需手动修改,可以通过下方 SetConfigurationOption 方法手动设置 Redis 链接信息(仅修改配置,不立即启用)
                 */
                Senparc.CO2NET.Cache.Redis.Register.SetConfigurationOption(redisConfigurationStr);
                Console.WriteLine("完成 Redis 设置");


                //以下会立即将全局缓存设置为 Redis
                Senparc.CO2NET.Cache.Redis.Register.UseKeyValueRedisNow();//键值对缓存策略(推荐)
                Console.WriteLine("启用 Redis UseKeyValue 策略");

                //Senparc.CO2NET.Cache.Redis.Register.UseHashRedisNow();//HashSet储存格式的缓存策略

                //也可以通过以下方式自定义当前需要启用的缓存策略
                //CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);//键值对
                //CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisHashSetObjectCacheStrategy.Instance);//HashSet
            }
            //如果这里不进行Redis缓存启用,则目前还是默认使用内存缓存

            #endregion                        // DPBMARK_END

            #region 配置和使用 Memcached      -- DPBMARK Memcached

            //配置Memcached缓存(按需,独立)
            var memcachedConfigurationStr = senparcSetting.Cache_Memcached_Configuration;
            var useMemcached = !string.IsNullOrEmpty(memcachedConfigurationStr) && memcachedConfigurationStr != "#{Cache_Memcached_Configuration}#";

            if (useMemcached) //这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略
            {
                /* 说明:
                 * 1、Memcached 的连接字符串信息会从 Config.SenparcSetting.Cache_Memcached_Configuration 自动获取并注册,如不需要修改,下方方法可以忽略
                 * /* 2、如需手动修改,可以通过下方 SetConfigurationOption 方法手动设置 Memcached 链接信息(仅修改配置,不立即启用)
                 */
                Senparc.CO2NET.Cache.Memcached.Register.SetConfigurationOption(memcachedConfigurationStr);
                Console.WriteLine("完成 Memcached 设置");

                //以下会立即将全局缓存设置为 Memcached
                Senparc.CO2NET.Cache.Memcached.Register.UseMemcachedNow();
                Console.WriteLine("启用 Memcached UseKeyValue 策略");


                //也可以通过以下方式自定义当前需要启用的缓存策略
                CacheStrategyFactory.RegisterObjectCacheStrategy(() => MemcachedObjectCacheStrategy.Instance);
                Console.WriteLine("立即启用 Memcached 策略");
            }

            #endregion                        //  DPBMARK_END

            #endregion

            #region 注册日志(按需,建议)

            register.RegisterTraceLog(ConfigTraceLog);//配置TraceLog

            #endregion

            #endregion

            Console.WriteLine("Hello CO2NET!");
            Console.WriteLine($"Total initialization time: {SystemTime.DiffTotalMS(dt1)}ms");

            Console.WriteLine($"当前缓存策略: {CacheStrategyFactory.GetObjectCacheStrategyInstance()}");

            Console.WriteLine($"SenparcSetting: {Config.SenparcSetting.ToJson(true)}");


            Console.ReadLine();
        }
Example #27
0
        /// <summary>
        /// 自动扫描并注册 ApiBind
        /// </summary>
        /// <param name="forceBindAgain">是否强制重刷新</param>
        internal static void AddApiBind(this IServiceCollection serviceCollection, bool forceBindAgain)
        {
            var dt1 = SystemTime.Now;

            //var cacheStragegy = CacheStrategyFactory.GetObjectCacheStrategyInstance();
            //using (cacheStragegy.BeginCacheLock("Senparc.NeuChar.Register", "RegisterApiBind"))
            lock (RegisterApiBindLock)//由于使用的是本地内存进行记录,所以这里不需要使用同步锁,这样就不需要依“缓存注册”等先决条件
            {
                if (RegisterApiBindFinished == true && forceBindAgain == false)
                {
                    Console.WriteLine($"RegisterApiBind has been finished, and doesn't require [forceBindAgain]. Quit build.");

                    return;
                }

                //查找所有扩展缓存
                var scanTypesCount = 0;
                var assembiles     = DependencyContext.Default.RuntimeLibraries.Select(z =>
                {
                    try
                    {
                        return(Assembly.Load(new AssemblyName(z.Name)));
                    }
                    catch
                    {
                        return(null);
                    }
                });

                //AppDomain.CurrentDomain.GetAssemblies();
                //Assembly.GetEntryAssembly().GetReferencedAssemblies().Select(Assembly.Load)
                //DependencyContext.Default.CompileLibraries.Select(z => Assembly.Load(z.Name))

                var errorCount = 0;

                //TODO:交给 CO2NET 底层统一扫描

                foreach (var assembly in assembiles)
                {
                    if (assembly == null)
                    {
                        continue;
                    }
                    var assemblyName = assembly.GetName().Name;
                    try
                    {
                        scanTypesCount++;
                        var classTypes = assembly.GetTypes()
                                         //.Where(z => z.Name.EndsWith("api", StringComparison.OrdinalIgnoreCase) ||
                                         //            z.Name.EndsWith("apis", StringComparison.OrdinalIgnoreCase))
                                         .ToArray();

                        foreach (var classType in classTypes)
                        {
                            if (/*type.IsAbstract || 静态类会被识别为 IsAbstract*/
                                !classType.IsPublic || !classType.IsClass || classType.IsEnum)
                            {
                                continue;
                            }

                            //判断 type 是否有 ApiBind 标记
                            var typeAttrs = classType.GetCustomAttributes(typeof(ApiBindAttribute), false)
                                            .Select(z => z as ApiBindAttribute).ToList();
                            var omitType = typeAttrs.FirstOrDefault(z => CheckOmitCategory(z, assemblyName)) != null;//默认忽略整个类


                            var coverAllMethods = false;              //class 上已经有覆盖所有方法的 [ApiBind] 特性标签
                            var hasChildApiAndNonStaticClass = false; //当前 class 内有需要被引用的对象(且当前 class 可以被实例化)

                            if (typeAttrs.Count() == 0 && CheckAdditionalClass(classType, out string classCategory))
                            {
                                typeAttrs.Add(new ApiBindAttribute(classCategory));//此类被运行时指定
                            }

                            if (typeAttrs.Count() > 0)
                            {
                                //type 中具有标记,所有的方法都归档
                                coverAllMethods = true;
                            }

                            var methods = classType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static /*| BindingFlags.Static | BindingFlags.InvokeMethod*/);

                            foreach (var method in methods)
                            {
                                var methodAttrs = method.GetCustomAttributes(typeof(ApiBindAttribute), false)
                                                  .Select(z => z as ApiBindAttribute).ToList();

                                var isApiMethod = methodAttrs.Count() > 0;//当前方法需要进行 API 处理

                                if (!isApiMethod && CheckAdditionalMethod(method, out string methodCategory))
                                {
                                    methodAttrs.Add(new ApiBindAttribute(methodCategory));                           //此方法被运行时指定,优先级最高,可以忽略其他忽略标签
                                }
                                else if (methodAttrs.FirstOrDefault(z => z.Ignore) != null ||                        //包含忽略参数
                                         method.GetCustomAttribute(typeof(IgnoreApiBindAttribute)) != null ||        //包含忽略标签
                                         methodAttrs.FirstOrDefault(z => CheckOmitCategory(z, assemblyName)) != null //被要求忽略
                                         )
                                {
                                    //主动标记忽略
                                    continue;
                                }

                                if (omitType &&
                                    methodAttrs.FirstOrDefault(z => CheckOmitCategory(z, assemblyName)) != null)
                                {
                                    //类已经被忽略,当前方法没有提供可以避开忽略的 Category,因此也被忽略
                                    continue;
                                }

                                //判断当前 class 是否包含可用的方法,如果包含,需要标记并进行后期处理
                                if (!hasChildApiAndNonStaticClass)
                                {
                                    hasChildApiAndNonStaticClass = coverAllMethods || isApiMethod;//TODO 注意忽略的对象
                                }

                                if (isApiMethod)
                                {
                                    //覆盖 classType 的绑定信息
                                    AddApiBindInfos(ApiBindOn.Method, methodAttrs, assemblyName, method);
                                    //TODO:检查需要忽略的对象
                                }
                                else if (coverAllMethods)
                                {
                                    //使用 classType 的绑定信息
                                    AddApiBindInfos(ApiBindOn.Method, typeAttrs, assemblyName, method);
                                }
                                else
                                {
                                    //忽略当前方法
                                }
                            }

                            var isStaticClass = classType.IsAbstract && classType.IsSealed;
                            if (hasChildApiAndNonStaticClass && !isStaticClass)
                            {
                                //当前类不是静态类,且内部方法被引用,需要进行 DI 配置
                                serviceCollection.AddScoped(classType);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        errorCount++;
                        SenparcTrace.SendCustomLog("RegisterApiBind() 自动扫描程序集报告(即使出现错误,非程序异常):" + assembly.FullName, ex.ToString());
                    }
                }

                RegisterApiBindFinished = true;

                Console.WriteLine($"RegisterApiBind Time: {SystemTime.DiffTotalMS(dt1)}ms, Api Count:{ApiBindInfoCollection.Instance.Count()}, Error Count: {errorCount}");
            }
        }
Example #28
0
        public void LockTest()
        {
            /* 测试逻辑:
             * 20个异步线程同时进行,
             * 线程内有两组不同的ResourceName: "Test-0","Test-1",模拟AccessTokenContainer和JsTicketContainer),
             * 每组ResourceName下面有两组分别为0和1的appId。
             * 同时运行这些线程,观察锁是否生效。
             * 失败的现象:1、同一个ResourceName、同一个appId下有两个线程同时获得锁
             *             2、不同的ResourceName、appId之间任意组合相互干扰;
             *             3、出现死锁;
             *             4、某线程始终无法获得锁(超时或一直运行)
             *
             * 注意:超时导致的失败可能是由于设置的最大等待时间相对于测试的Thread.Sleep(sleepMillionSeconds)太短。
             */

            Console.WriteLine("同步方法测试");

            bool useRedis = true;

            if (useRedis)
            {
                var redisConfiguration = "localhost:6379";
                RedisManager.ConfigurationOption = redisConfiguration;
                CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);//Redis
            }

            Console.WriteLine($"使用缓存策略:{CacheStrategyFactory.GetObjectCacheStrategyInstance()}");

            Random rnd                 = new Random();
            var    threadsCount        = 20M;
            int    sleepMillionSeconds = 200;//数字越大,每个线程占用时间越长,直至超时

            //初步估计需要重试时间
            var differentLocksCount = (2 /*appId*/ * 2 /*resource*/) /* = diffrent locks count */;
            var runCycle            = threadsCount / differentLocksCount /* = Run Cycle*/;
            var hopedTotalTime      = runCycle * (sleepMillionSeconds + 100) /* = Hoped Total Time */;
            var retryDelay          = 20; /* = retryDelayCycle MillionSeconds */
            var randomRetryDelay    = (retryDelay / 2) /*random retry delay*/;
            var retryTimes          = 50; // hopedTotalTime / randomRetryDelay; /* = maxium retry times */;//此数字可以调整(根据测试结果逐步缩小,可以看到会有失败的锁产生)

            Console.WriteLine("sleepMillionSeconds:{0}ms", sleepMillionSeconds);
            Console.WriteLine("differentLocksCount:{0}", differentLocksCount);
            Console.WriteLine("runCycle:{0}", runCycle);
            Console.WriteLine("hopedTotalTime:{0}ms", hopedTotalTime);
            Console.WriteLine("randomRetryDelay:{0}ms", randomRetryDelay);
            Console.WriteLine("retryTimes:{0}", retryTimes);
            Console.WriteLine("randomLockTime(可能的Lock超时时间):{0}ms", randomRetryDelay * retryTimes);
            Console.WriteLine("=====================");

            List <Thread> list = new List <Thread>();

            int[] runThreads = { 0 };
            var   dt0        = SystemTime.Today;

            for (int i = 0; i < (int)threadsCount; i++)
            {
                runThreads[0]++;
                var i1     = i;
                var thread = new Thread(() =>
                {
                    var appId        = (i1 % 2).ToString();
                    var resourceName = "Test-" + rnd.Next(0, 2);                              //调整这里的随机数,可以改变锁的个数
                    var cache        = CacheStrategyFactory.GetObjectCacheStrategyInstance(); //每次重新获取实例(因为单例模式,所以其实是同一个)

                    Console.WriteLine($"线程 {i1} / {resourceName} : {appId} 进入,准备尝试锁。Cache实例:{cache.GetHashCode()}");

                    var dt1 = SystemTime.Now;
                    using (var cacheLock = cache.BeginCacheLock(resourceName, appId, (int)retryTimes, TimeSpan.FromMilliseconds(retryDelay)))
                    {
                        var result = cacheLock.LockSuccessful
                            ? "成功"
                            : "【失败!】";
                        result += " | TTL:" + cacheLock.GetTotalTtl(retryTimes, TimeSpan.FromMilliseconds(retryDelay)) + "ms";

                        Console.WriteLine($"线程 {i1} / {resourceName} : {appId} 进入锁,等待时间:{SystemTime.DiffTotalMS(dt1)} / {SystemTime.DiffTotalMS(dt0)}ms,获得锁结果:{result}");

                        Thread.Sleep(sleepMillionSeconds);
                    }
                    Console.WriteLine($"线程 {i1} / {resourceName} : {appId} 释放锁({SystemTime.DiffTotalMS(dt1)}ms)");
                    runThreads[0]--;
                });
                list.Add(thread);
            }


            var dtAll1 = SystemTime.Now;

            list.ForEach(z => z.Start());

            while (runThreads[0] > 0)
            {
                Thread.Sleep(10);
            }

            Console.WriteLine("Real Time:{0}ms", SystemTime.DiffTotalMS(dtAll1));

            //Thread.Sleep((int)hopedTotalTime + 1000);
            //while (true)
            //{
            //    Thread.Sleep(10);
            //    if (list.Count(z => z.ThreadState == ThreadState.Aborted) == list.Count())
            //    {
            //        break;
            //    }
            //}

            //等待
            //Parallel.For(0, (int)threadsCount, i =>
            //{
            //    var appId = (i % 2).ToString();
            //    var resourceName = "Test-" + rnd.Next(0, 2);
            //    Console.WriteLine("线程 {0} / {1} : {2} 进入,准备尝试锁", Thread.CurrentThread.GetHashCode(), resourceName, appId);

            //    DateTime dt1 = SystemTime.Now;
            //    using (var cacheLock = Cache.BeginCacheLock(resourceName, appId, (int)retryTimes, new TimeSpan(0, 0, 0, 0, 20)))
            //    {
            //        var result = cacheLock.LockSuccessful
            //            ? "成功"
            //            : "【失败!】";

            //        Console.WriteLine("线程 {0} / {1} : {2} 进入锁,等待时间:{3}ms,获得锁结果:{4}", Thread.CurrentThread.GetHashCode(), resourceName, appId, SystemTime.DiffTotalMS(dt1), result);

            //        Thread.Sleep(sleepMillionSeconds);
            //    }
            //    Console.WriteLine("线程 {0} / {1} : {2} 释放锁({3}ms)", Thread.CurrentThread.GetHashCode(), resourceName, appId, SystemTime.DiffTotalMS(dt1));
            //});
        }
Example #29
0
        /// <summary>
        /// 获取并清空该 Domain 下的所有数据
        /// </summary>
        /// <returns></returns>
        /// <param name="removeReadItems">是否移除已读取的项目,默认为 true</param>
        /// <param name="keepTodayData">当 removeReadItems = true 时有效,在清理的时候是否保留当天的数据</param>
        public async Task <List <MinuteDataPack> > ReadAndCleanDataItemsAsync(bool removeReadItems = true, bool keepTodayData = true)
        {
            try
            {
                var dt1 = SystemTime.Now;

                var cacheStragety = Cache.CacheStrategyFactory.GetObjectCacheStrategyInstance();
                Dictionary <string, List <DataItem> > tempDataItems = new Dictionary <string, List <DataItem> >();

                var systemNow     = SystemTime.Now.UtcDateTime;                                                                     //统一UTC时间
                var nowMinuteTime = SystemTime.Now.AddSeconds(-SystemTime.Now.Second).AddMilliseconds(-SystemTime.Now.Millisecond); // new DateTimeOffset(systemNow.Year, systemNow.Month, systemNow.Day, systemNow.Hour, systemNow.Minute, 0, TimeSpan.Zero);

                //快速获取并清理数据
                foreach (var item in KindNameStore[_domain])
                {
                    var kindName = item.Key;
                    var finalKey = BuildFinalKey(kindName);
                    using (await cacheStragety.BeginCacheLockAsync("SenparcAPM", finalKey).ConfigureAwait(false))
                    {
                        var list = await GetDataItemListAsync(item.Key).ConfigureAwait(false);        //获取列表

                        var completedStatData = list.Where(z => z.DateTime < nowMinuteTime).ToList(); //统计范围内的所有数据

                        tempDataItems[kindName] = completedStatData;                                  //添加到列表

                        if (removeReadItems)
                        {
                            //筛选需要删除的数据
                            var tobeRemove = completedStatData.Where(z => keepTodayData ? z.DateTime < SystemTime.Today : true);

                            //移除已读取的项目
                            if (tobeRemove.Count() == list.Count())
                            {
                                //已经全部删除
                                await cacheStragety.RemoveFromCacheAsync(finalKey, true).ConfigureAwait(false);//删除
                            }
                            else
                            {
                                //部分删除
                                var newList = list.Except(tobeRemove).ToList();
                                await cacheStragety.SetAsync(finalKey, newList, Config.DataExpire, true).ConfigureAwait(false);
                            }
                        }
                    }
                }


                //开始处理数据(分两步是为了减少同步锁的时间)
                var result = new List <MinuteDataPack>();
                foreach (var kv in tempDataItems)
                {
                    var kindName   = kv.Key;
                    var domainData = kv.Value;

                    var lastDataItemTime = DateTimeOffset.MinValue;

                    MinuteDataPack minuteDataPack = new MinuteDataPack();
                    minuteDataPack.KindName = kindName;
                    result.Add(minuteDataPack);   //添加一个指标

                    MinuteData minuteData = null; //某一分钟的指标
                    foreach (var dataItem in domainData)
                    {
                        if (DataHelper.IsLaterMinute(lastDataItemTime, dataItem.DateTime))
                        {
                            //新的一分钟
                            minuteData = new MinuteData();
                            minuteDataPack.MinuteDataList.Add(minuteData);

                            minuteData.KindName     = dataItem.KindName;
                            minuteData.Time         = new DateTimeOffset(dataItem.DateTime.Year, dataItem.DateTime.Month, dataItem.DateTime.Day, dataItem.DateTime.Hour, dataItem.DateTime.Minute, 0, TimeSpan.Zero);
                            minuteData.StartValue   = dataItem.Value;
                            minuteData.HighestValue = dataItem.Value;
                            minuteData.LowestValue  = dataItem.Value;
                        }

                        minuteData.EndValue  = dataItem.Value;
                        minuteData.SumValue += dataItem.Value;

                        if (dataItem.Value > minuteData.HighestValue)
                        {
                            minuteData.HighestValue = dataItem.Value;
                        }

                        if (dataItem.Value < minuteData.LowestValue)
                        {
                            minuteData.LowestValue = dataItem.Value;
                        }


                        minuteData.SampleSize++;

                        lastDataItemTime = dataItem.DateTime;
                    }
                }

                //if (SenparcTrace.RecordAPMLog)
                {
                    SenparcTrace.SendCustomLog("APM 记录 - DataOperation.ReadAndCleanDataItems", SystemTime.DiffTotalMS(dt1) + " ms");
                }

                return(result);
            }
            catch (Exception e)
            {
                new APMException(e.Message, _domain, "", "DataOperation.ReadAndCleanDataItems");
                return(null);
            }
        }
Example #30
0
        public ActionResult Index(string codesStr)
        {
            var codes = GetCodes(codesStr);

            var dt0 = SystemTime.Now;

            //QrCode 根目录
            var qrCodeDir = Path.Combine(CO2NET.Config.RootDictionaryPath, "App_Data", "QrCode");

            if (!Directory.Exists(qrCodeDir))
            {
                Directory.CreateDirectory(qrCodeDir);
            }

            //定义文件名和路径
            var tempId              = SystemTime.Now.ToString("yyyy-MM-dd-HHmmss"); //本次生成唯一Id
            var tempDirName         = $"{tempId}_{Guid.NewGuid().ToString("n")}";   //临时文件夹名
            var tempZipFileName     = $"{tempDirName}.zip";                         //临时压缩文件名
            var tempZipFileFullPath = Path.Combine(qrCodeDir, tempZipFileName);     //临时压缩文件完整路径
            var tempDir             = Path.Combine(qrCodeDir, tempDirName);

            Directory.CreateDirectory(tempDir);//创建临时目录

            //说明文件
            var readmeFile = Path.Combine(qrCodeDir, "readme.txt");

            System.IO.File.Copy(readmeFile, Path.Combine(tempDir, "readme.txt"));

            //便利所有二维码内容
            var i = 0;

            foreach (var code in codes)
            {
                if (code.IsNullOrEmpty())
                {
                    continue; //过滤为空的段落
                }
                i++;          //计数器


                var finalCode = code.Length > 100 ? code.Substring(0, 100) : code;//约束长度

                //二维码生成开始
                BitMatrix bitMatrix;//定义像素矩阵对象
                bitMatrix = new MultiFormatWriter().encode(finalCode, BarcodeFormat.QR_CODE /*条码或二维码标准*/, 600 /*宽度*/, 600 /*高度*/);
                var bw = new ZXing.BarcodeWriterPixelData();

                var pixelData = bw.Write(bitMatrix);
                var bitmap    = new System.Drawing.Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);//预绘制32bit标准的位图片

                var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, pixelData.Width, pixelData.Height) /*绘制矩形区域及偏移量*/,
                                                 System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
                try
                {
                    //假设位图的 row stride = 4 字节 * 图片的宽度
                    System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0, pixelData.Pixels.Length);
                }
                finally
                {
                    bitmap.UnlockBits(bitmapData);//从内存 Unlock bitmap 对象
                }
                //二维码生成结束

                var fileName = Path.Combine(tempDir, $"{i}.jpg");//二维码文件名

                //保存二维码
                var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);
                {
                    bitmap.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png);
                    fileStream.Close();//一定要关闭文件流
                }
            }
            SenparcTrace.SendCustomLog("二维码生成结束", $"耗时:{SystemTime.DiffTotalMS(dt0)} ms,临时文件:{tempZipFileFullPath}");//记录日志

            var dt1 = SystemTime.Now;

            while (Directory.GetFiles(tempDir).Length < i + 1 /* readme.txt */ && SystemTime.NowDiff(dt1) < TimeSpan.FromSeconds(30) /*最多等待时间*/)
            {
                Thread.Sleep(1000);//重试等待时间
            }

            ZipFile.CreateFromDirectory(tempDir, tempZipFileFullPath, CompressionLevel.Fastest, false);//创建压缩文件

            var dt2 = SystemTime.Now;

            while (SystemTime.NowDiff(dt2) < TimeSpan.FromSeconds(10) /*最多等待时间*/)
            {
                FileStream fs = null;
                try
                {
                    fs = new FileStream(tempZipFileFullPath, FileMode.Open, FileAccess.Read, FileShare.None);
                }
                catch
                {
                    Thread.Sleep(500);
                    continue;
                }

                if (fs != null)
                {
                    return(File(fs, "application/x-zip-compressed", $"SenparcQrCode_{tempId}.zip"));
                    //TODO:删除临时文件
                }
            }

            return(Content("打包文件失败!"));
        }