diff --git a/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs b/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs index e1261f1..b626b8c 100644 --- a/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs +++ b/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs @@ -188,9 +188,9 @@ public virtual async Task Notify() if (HttpContext.WebSockets.IsWebSocketRequest) { - using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync(); + using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); - await HandleNotify(socket, Token); + await HandleNotify(socket, Token).ConfigureAwait(false); } else HttpContext.Response.StatusCode = 400; @@ -231,7 +231,7 @@ await socket.WaitForClose(txt => // 长连接上线。可能客户端心跳已经停了,WS还在,这里重新上线 _deviceService.SetOnline(device, true, token, ip); } - }, source); + }, source).ConfigureAwait(false); WriteLog("WebSocket断开", true, $"State={socket.State} CloseStatus={socket.CloseStatus} sid={sid} Remote={remote}"); @@ -248,7 +248,7 @@ private async Task ConsumeMessage(WebSocket socket, String code, IProducerConsum while (!cancellationToken.IsCancellationRequested && socket.State == WebSocketState.Open) { ISpan? span = null; - var mqMsg = await queue.TakeOneAsync(15, cancellationToken); + var mqMsg = await queue.TakeOneAsync(15, cancellationToken).ConfigureAwait(false); if (mqMsg != null) { // 埋点 @@ -274,14 +274,14 @@ private async Task ConsumeMessage(WebSocket socket, String code, IProducerConsum { WriteLog("WebSocket发送", true, mqMsg); - await socket.SendAsync(mqMsg.GetBytes(), WebSocketMessageType.Text, true, cancellationToken); + await socket.SendAsync(mqMsg.GetBytes(), WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); } span?.Dispose(); } else { - await Task.Delay(1_000, cancellationToken); + await Task.Delay(1_000, cancellationToken).ConfigureAwait(false); } } } diff --git a/NewLife.Remoting.Extensions/ModelBinders/InterfaceModelBinder.cs b/NewLife.Remoting.Extensions/ModelBinders/InterfaceModelBinder.cs index 939dcec..6a41a74 100644 --- a/NewLife.Remoting.Extensions/ModelBinders/InterfaceModelBinder.cs +++ b/NewLife.Remoting.Extensions/ModelBinders/InterfaceModelBinder.cs @@ -19,7 +19,7 @@ public async Task BindModelAsync(ModelBindingContext bindingContext) try { var req = bindingContext.HttpContext.Request; - var entityBody = await req.ReadFromJsonAsync(model!.GetType()); + var entityBody = await req.ReadFromJsonAsync(model!.GetType()).ConfigureAwait(false); bindingContext.Result = ModelBindingResult.Success(entityBody); } diff --git a/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj b/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj index 910f014..93596ec 100644 --- a/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj +++ b/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj @@ -20,6 +20,7 @@ True ..\Doc\newlife.snk 1701;1702;NU5104;NETSDK1138;CS7035 + CA2007 diff --git a/NewLife.Remoting/Clients/ClientBase.cs b/NewLife.Remoting/Clients/ClientBase.cs index 8f14da0..ad49a5e 100644 --- a/NewLife.Remoting/Clients/ClientBase.cs +++ b/NewLife.Remoting/Clients/ClientBase.cs @@ -262,7 +262,7 @@ class MyApiClient : ApiClient { public ClientBase Client { get; set; } = null!; - protected override async Task OnLoginAsync(ISocketClient client, Boolean force, CancellationToken cancellationToken) => await InvokeWithClientAsync(client, Client.Actions[Features.Login], Client.BuildLoginRequest(), 0, cancellationToken); + protected override Task OnLoginAsync(ISocketClient client, Boolean force, CancellationToken cancellationToken) => InvokeWithClientAsync(client, Client.Actions[Features.Login], Client.BuildLoginRequest(), 0, cancellationToken); } /// 异步调用。HTTP默认POST,自动识别GET @@ -284,10 +284,10 @@ protected virtual async Task OnInvokeAsync(String action, Obje if (args == null || args.GetType().IsBaseType() || action.StartsWithIgnoreCase("Get") || action.ToLower().Contains("/get")) method = HttpMethod.Get; - rs = await http.InvokeAsync(method, action, args, null, cancellationToken); + rs = await http.InvokeAsync(method, action, args, null, cancellationToken).ConfigureAwait(false); } else - rs = await _client.InvokeAsync(action, args, cancellationToken); + rs = await _client.InvokeAsync(action, args, cancellationToken).ConfigureAwait(false); if (Log != null && Log.Level <= LogLevel.Debug) WriteLog("[{0}]<={1}", action, rs is IPacket or Byte[]? "" : rs?.ToJson()); @@ -311,10 +311,10 @@ protected virtual async Task GetAsync(String action, Object? a // 验证登录 var needLogin = !Actions[Features.Login].EqualIgnoreCase(action); - if (!Logined && needLogin && Features.HasFlag(Features.Login)) await Login(cancellationToken); + if (!Logined && needLogin && Features.HasFlag(Features.Login)) await Login(cancellationToken).ConfigureAwait(false); // GET请求 - var rs = await http.InvokeAsync(HttpMethod.Get, action, args, null, cancellationToken); + var rs = await http.InvokeAsync(HttpMethod.Get, action, args, null, cancellationToken).ConfigureAwait(false); if (Log != null && Log.Level <= LogLevel.Debug) WriteLog("[{0}]<={1}", action, rs is IPacket or Byte[]? "" : rs?.ToJson()); @@ -334,11 +334,11 @@ protected virtual async Task GetAsync(String action, Object? a { // 验证登录。如果该接口需要登录,且未登录,则先登录 var needLogin = !Actions[Features.Login].EqualIgnoreCase(action); - if (needLogin && !Logined && Features.HasFlag(Features.Login)) await Login(cancellationToken); + if (needLogin && !Logined && Features.HasFlag(Features.Login)) await Login(cancellationToken).ConfigureAwait(false); try { - return await OnInvokeAsync(action, args, cancellationToken); + return await OnInvokeAsync(action, args, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -353,10 +353,10 @@ protected virtual async Task GetAsync(String action, Object? a { Log?.Debug("{0}", ex); WriteLog("重新登录,因调用[{0}]失败:{1}", action, ex.Message); - await Login(cancellationToken); + await Login(cancellationToken).ConfigureAwait(false); // 再次执行当前请求 - return await OnInvokeAsync(action, args, cancellationToken); + return await OnInvokeAsync(action, args, cancellationToken).ConfigureAwait(false); } } @@ -410,7 +410,7 @@ private async Task TryConnectServer(Object state) var timer = _timerLogin; try { - if (!Logined) await Login(); + if (!Logined) await Login().ConfigureAwait(false); } catch (Exception ex) { @@ -443,7 +443,7 @@ private async Task TryConnectServer(Object state) { for (var i = 0; i < 50; i++) { - await TaskEx.Delay(100); + await TaskEx.Delay(100, cancellationToken).ConfigureAwait(false); if (Status == LoginStatus.LoggedIn) return null; if (Status != LoginStatus.LoggingIn) break; } @@ -465,7 +465,7 @@ private async Task TryConnectServer(Object state) // 登录前清空令牌,避免服务端使用上一次信息 SetToken(null); - response = await LoginAsync(request, cancellationToken); + response = await LoginAsync(request, cancellationToken).ConfigureAwait(false); if (response == null) return null; WriteLog("登录成功:{0}", response); @@ -586,7 +586,7 @@ protected virtual void FillLoginRequest(ILoginRequest request) try { - var rs = await LogoutAsync(reason, cancellationToken); + var rs = await LogoutAsync(reason, cancellationToken).ConfigureAwait(false); // 更新令牌 SetToken(rs?.Token); @@ -617,9 +617,9 @@ protected virtual void FillLoginRequest(ILoginRequest request) protected virtual async Task LogoutAsync(String? reason, CancellationToken cancellationToken) { if (_client is ApiHttpClient) - return await GetAsync(Actions[Features.Logout], new { reason }, cancellationToken); + return await GetAsync(Actions[Features.Logout], new { reason }, cancellationToken).ConfigureAwait(false); - return await InvokeAsync(Actions[Features.Logout], new { reason }, cancellationToken); + return await InvokeAsync(Actions[Features.Logout], new { reason }, cancellationToken).ConfigureAwait(false); } #endregion @@ -651,7 +651,7 @@ protected virtual void FillLoginRequest(ILoginRequest request) IPingResponse? response = null; try { - response = await PingAsync(request, cancellationToken); + response = await PingAsync(request, cancellationToken).ConfigureAwait(false); if (response != null) { // 由服务器改变采样频率 @@ -667,7 +667,7 @@ protected virtual void FillLoginRequest(ILoginRequest request) { foreach (var model in response.Commands) { - await ReceiveCommand(model, "Pong", cancellationToken); + await ReceiveCommand(model, "Pong", cancellationToken).ConfigureAwait(false); } } } @@ -684,7 +684,7 @@ protected virtual void FillLoginRequest(ILoginRequest request) // 上报正常,处理历史,失败则丢弃 while (_fails.TryDequeue(out var info)) { - await PingAsync(info, cancellationToken); + await PingAsync(info, cancellationToken).ConfigureAwait(false); } return response; @@ -700,7 +700,7 @@ protected virtual void FillLoginRequest(ILoginRequest request) if (Features.HasFlag(Features.Login)) { WriteLog("重新登录,因心跳失败:{0}", ex.Message); - await Login(cancellationToken); + await Login(cancellationToken).ConfigureAwait(false); } return null; @@ -776,14 +776,14 @@ private async Task CheckUpgrade(Object? data) { if (!NetworkInterface.GetIsNetworkAvailable()) return; - await Upgrade(null); + await Upgrade(null).ConfigureAwait(false); } private async Task ReceiveUpgrade(String? arguments) { // 参数作为通道 var channel = arguments; - var rs = await Upgrade(channel); + var rs = await Upgrade(channel).ConfigureAwait(false); if (rs == null) return "没有可用更新!"; return $"成功更新到[{rs.Version}]"; @@ -802,7 +802,7 @@ private async Task CheckUpgrade(Object? data) ug.DeleteBackup("."); // 调用接口查询思否存在更新信息 - var info = await UpgradeAsync(channel, cancellationToken); + var info = await UpgradeAsync(channel, cancellationToken).ConfigureAwait(false); if (info == null || info.Version == _lastVersion) return info; // _lastVersion避免频繁更新同一个版本 @@ -813,7 +813,7 @@ private async Task CheckUpgrade(Object? data) { // 下载文件包 ug.Url = BuildUrl(info.Source!); - await ug.Download(cancellationToken); + await ug.Download(cancellationToken).ConfigureAwait(false); // 检查文件完整性 if (!info.FileHash.IsNullOrEmpty() && !ug.CheckFileHash(info.FileHash)) @@ -905,9 +905,9 @@ protected virtual void Restart(Upgrade upgrade) protected virtual async Task UpgradeAsync(String? channel, CancellationToken cancellationToken) { if (_client is ApiHttpClient) - return await GetAsync(Actions[Features.Upgrade], new { channel }, cancellationToken); + return await GetAsync(Actions[Features.Upgrade], new { channel }, cancellationToken).ConfigureAwait(false); - return await InvokeAsync(Actions[Features.Upgrade], new { channel }, cancellationToken); + return await InvokeAsync(Actions[Features.Upgrade], new { channel }, cancellationToken).ConfigureAwait(false); } #endregion @@ -958,7 +958,7 @@ protected virtual async Task OnPing(Object state) using var span = Tracer?.NewSpan(Name + "Ping"); try { - if (Features.HasFlag(Features.Ping)) await Ping(); + if (Features.HasFlag(Features.Ping)) await Ping().ConfigureAwait(false); if (_client is ApiHttpClient http && Features.HasFlag(Features.Notify)) { @@ -968,7 +968,7 @@ protected virtual async Task OnPing(Object state) #else _ws ??= new WsChannel(this); #endif - if (_ws != null) await _ws.ValidWebSocket(http); + if (_ws != null) await _ws.ValidWebSocket(http).ConfigureAwait(false); } } catch (Exception ex) @@ -1024,19 +1024,19 @@ protected virtual async Task OnPing(Object state) }; if (Features.HasFlag(Features.CommandReply)) - await CommandReply(reply, cancellationToken); + await CommandReply(reply, cancellationToken).ConfigureAwait(false); return reply; } else - return await OnReceiveCommand(model, cancellationToken); + return await OnReceiveCommand(model, cancellationToken).ConfigureAwait(false); } else { var reply = new CommandReplyModel { Id = model.Id, Status = CommandStatus.取消 }; if (Features.HasFlag(Features.CommandReply)) - await CommandReply(reply, cancellationToken); + await CommandReply(reply, cancellationToken).ConfigureAwait(false); return reply; } @@ -1057,11 +1057,11 @@ protected virtual async Task OnPing(Object state) var e = new CommandEventArgs { Model = model }; Received?.Invoke(this, e); - var rs = await this.ExecuteCommand(model, cancellationToken); + var rs = await this.ExecuteCommand(model, cancellationToken).ConfigureAwait(false); e.Reply ??= rs; if (e.Reply != null && e.Reply.Id > 0 && Features.HasFlag(Features.CommandReply)) - await CommandReply(e.Reply, cancellationToken); + await CommandReply(e.Reply, cancellationToken).ConfigureAwait(false); return e.Reply; } @@ -1071,7 +1071,7 @@ protected virtual async Task OnPing(Object state) /// /// /// - public virtual async Task SendCommand(String command, String argument, CancellationToken cancellationToken = default) => await OnReceiveCommand(new CommandModel { Command = command, Argument = argument }, cancellationToken); + public virtual Task SendCommand(String command, String argument, CancellationToken cancellationToken = default) => OnReceiveCommand(new CommandModel { Command = command, Argument = argument }, cancellationToken); /// 上报命令调用结果 /// @@ -1120,7 +1120,7 @@ async Task DoPostEvent(Object state) if (tid != null) span?.Detach(tid); try { - if (list.Count > 0) await PostEvents(list.ToArray()); + if (list.Count > 0) await PostEvents(list.ToArray()).ConfigureAwait(false); // 成功后读取本地缓存 while (_failEvents.TryDequeue(out var ev)) diff --git a/NewLife.Remoting/Clients/ICommandClient.cs b/NewLife.Remoting/Clients/ICommandClient.cs index c53d597..60c6e6e 100644 --- a/NewLife.Remoting/Clients/ICommandClient.cs +++ b/NewLife.Remoting/Clients/ICommandClient.cs @@ -112,7 +112,7 @@ public static async Task ExecuteCommand(this ICommandClient c var rs = new CommandReplyModel { Id = model.Id, Status = CommandStatus.已完成 }; try { - var result = await OnCommand(client, model, cancellationToken); + var result = await OnCommand(client, model, cancellationToken).ConfigureAwait(false); if (result is CommandReplyModel reply) { reply.Id = model.Id; @@ -152,16 +152,17 @@ public static async Task ExecuteCommand(this ICommandClient c if (!client.Commands.TryGetValue(model.Command, out var d)) throw new ApiException(ApiCode.NotFound, $"找不到服务[{model.Command}]"); - if (d is Func> func1) return await func1(model.Argument); - //if (d is Func> func2) return await func2(model.Argument); - if (d is Func> func3) return await func3(model); - if (d is Func> func4) return await func4(model, cancellationToken); + if (d is Func> func1) + return await func1(model.Argument).ConfigureAwait(false); + if (d is Func> func3) + return await func3(model).ConfigureAwait(false); + if (d is Func> func4) + return await func4(model, cancellationToken).ConfigureAwait(false); if (d is Action func21) func21(model); if (d is Func func31) return func31(model); if (d is Func func32) return func32(model.Argument); - //if (d is Func func33) return func33(model.Argument); //return null; throw new ApiException(ApiCode.InternalServerError, $"服务[{model.Command}]的签名[{d}]不正确"); diff --git a/NewLife.Remoting/Clients/Upgrade.cs b/NewLife.Remoting/Clients/Upgrade.cs index 735ef2e..1a3b072 100644 --- a/NewLife.Remoting/Clients/Upgrade.cs +++ b/NewLife.Remoting/Clients/Upgrade.cs @@ -64,8 +64,7 @@ public virtual async Task Download(CancellationToken cancellationToken var sw = Stopwatch.StartNew(); var web = CreateClient(); - //await web.DownloadFileAsync(url, file); - file = await DownloadFileAsync(web, url, file, cancellationToken); + file = await DownloadFileAsync(web, url, file, cancellationToken).ConfigureAwait(false); sw.Stop(); WriteLog("下载完成!{2} 大小{0:n0}字节,耗时{1:n0}ms", file.AsFile().Length, sw.ElapsedMilliseconds, file); @@ -341,7 +340,7 @@ private HttpClient CreateClient() public static async Task DownloadFileAsync(HttpClient client, String address, String fileName, CancellationToken cancellationToken = default) { var request = new HttpRequestMessage(HttpMethod.Get, address); - var rs = await client.SendAsync(request, cancellationToken); + var rs = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); rs.EnsureSuccessStatusCode(); // 从Http响应头中获取文件名 @@ -359,7 +358,8 @@ public static async Task DownloadFileAsync(HttpClient client, String add var ms = rs.Content; using var fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); - await ms.CopyToAsync(fs); + await ms.CopyToAsync(fs).ConfigureAwait(false); + await fs.FlushAsync(cancellationToken).ConfigureAwait(false); // 截断文件,如果前面删除失败,这里就可能使用旧文件,需要把多余部分截断 fs.SetLength(fs.Position); diff --git a/NewLife.Remoting/Clients/WsChannel.cs b/NewLife.Remoting/Clients/WsChannel.cs index 4290f3a..6372225 100644 --- a/NewLife.Remoting/Clients/WsChannel.cs +++ b/NewLife.Remoting/Clients/WsChannel.cs @@ -43,7 +43,7 @@ public virtual async Task ValidWebSocket(ApiHttpClient http) try { // 在websocket链路上定时发送心跳,避免长连接被断开 - await _websocket.SendTextAsync("Ping"); + await _websocket.SendTextAsync("Ping").ConfigureAwait(false); } catch (Exception ex) { @@ -85,20 +85,20 @@ private async Task DoPull(WebSocketClient socket, CancellationTokenSource source var buf = new Byte[64 * 1024]; while (!source.IsCancellationRequested && !socket.Disposed) { - using var rs = await socket.ReceiveMessageAsync(source.Token); + using var rs = await socket.ReceiveMessageAsync(source.Token).ConfigureAwait(false); if (rs == null) continue; if (rs.Type == WebSocketMessageType.Close) break; if (rs.Type == WebSocketMessageType.Text) { var txt = rs.Payload?.ToStr(); - if (txt != null) await OnReceive(txt); + if (txt != null) await OnReceive(txt).ConfigureAwait(false); } } if (!source.IsCancellationRequested) source.Cancel(); - if (!socket.Disposed) await socket.CloseAsync(1000, "finish", default); + if (!socket.Disposed) await socket.CloseAsync(1000, "finish", default).ConfigureAwait(false); } catch (TaskCanceledException) { } catch (OperationCanceledException) { } @@ -123,7 +123,7 @@ private async Task OnReceive(String message) else { var model = message.ToJsonEntity(); - if (model != null) await _client.ReceiveCommand(model, "WebSocket"); + if (model != null) await _client.ReceiveCommand(model, "WebSocket").ConfigureAwait(false); } } @@ -134,7 +134,7 @@ private void StopWebSocket() try { if (_websocket != null && !_websocket.Disposed) - _websocket.CloseAsync(1000, "finish", default); + _websocket.CloseAsync(1000, "finish", default).Wait(1000); } catch { } diff --git a/NewLife.Remoting/Clients/WsChannelCore.cs b/NewLife.Remoting/Clients/WsChannelCore.cs index e4a981e..54f0cc1 100644 --- a/NewLife.Remoting/Clients/WsChannelCore.cs +++ b/NewLife.Remoting/Clients/WsChannelCore.cs @@ -42,7 +42,7 @@ public override async Task ValidWebSocket(ApiHttpClient http) { // 在websocket链路上定时发送心跳,避免长连接被断开 var str = "Ping"; - await _websocket.SendAsync(new ArraySegment(str.GetBytes()), WebSocketMessageType.Text, true, default); + await _websocket.SendAsync(new ArraySegment(str.GetBytes()), WebSocketMessageType.Text, true, default).ConfigureAwait(false); } catch (Exception ex) { @@ -65,7 +65,7 @@ public override async Task ValidWebSocket(ApiHttpClient http) client.Options.SetRequestHeader("Authorization", "Bearer " + token); span?.AppendTag($"WebSocket.Connect {uri}"); - await client.ConnectAsync(uri, default); + await client.ConnectAsync(uri, default).ConfigureAwait(false); _websocket = client; @@ -84,19 +84,19 @@ private async Task DoPull(WebSocket socket, CancellationTokenSource source) var buf = new Byte[64 * 1024]; while (!source.IsCancellationRequested && socket.State == WebSocketState.Open) { - var data = await socket.ReceiveAsync(new ArraySegment(buf), source.Token); + var data = await socket.ReceiveAsync(new ArraySegment(buf), source.Token).ConfigureAwait(false); if (data.MessageType == WebSocketMessageType.Close) break; if (data.MessageType == WebSocketMessageType.Text) { var txt = buf.ToStr(null, 0, data.Count); - if (txt != null) await OnReceive(txt); + if (txt != null) await OnReceive(txt).ConfigureAwait(false); } } if (!source.IsCancellationRequested) source.Cancel(); if (socket.State == WebSocketState.Open) - await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "finish", default); + await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "finish", default).ConfigureAwait(false); } catch (TaskCanceledException) { } catch (OperationCanceledException) { } @@ -120,8 +120,8 @@ private async Task OnReceive(String message) } else { - var model = _client.JsonHost.Read(message, typeof(CommandModel)) as CommandModel; - if (model != null) await _client.ReceiveCommand(model, "WebSocket"); + var model = _client.JsonHost.Read(message); + if (model != null) await _client.ReceiveCommand(model, "WebSocket").ConfigureAwait(false); } } diff --git a/NewLife.Remoting/NewLife.Remoting.csproj b/NewLife.Remoting/NewLife.Remoting.csproj index eaa9acc..9a10f77 100644 --- a/NewLife.Remoting/NewLife.Remoting.csproj +++ b/NewLife.Remoting/NewLife.Remoting.csproj @@ -18,6 +18,7 @@ True ..\Doc\newlife.snk 1701;1702;NU5104;NETSDK1138;CS7035 + CA2007 @@ -52,7 +53,7 @@ - + diff --git a/NewLife.Remoting/WsClient.cs b/NewLife.Remoting/WsClient.cs index 4f03f2a..4209c63 100644 --- a/NewLife.Remoting/WsClient.cs +++ b/NewLife.Remoting/WsClient.cs @@ -243,10 +243,10 @@ public virtual Int32 InvokeOneWay(String action, Object? args = null, Byte flag var codec = GetMessageCodec(); var context = new NetHandlerContext(); var pk = codec.Write(context, msg) as IPacket; - await client.SendAsync(pk!.ToSegment(), WebSocketMessageType.Binary, true, default); + await client.SendAsync(pk!.ToSegment(), WebSocketMessageType.Binary, true, default).ConfigureAwait(false); var buf = new Byte[64 * 1024]; - var data = await client.ReceiveAsync(new ArraySegment(buf), default); + var data = await client.ReceiveAsync(new ArraySegment(buf), default).ConfigureAwait(false); rs = codec.Read(context, data) as IMessage; if (rs == null) return default; diff --git a/Test/Test.csproj b/Test/Test.csproj index 79ea4e0..c3ac54f 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -10,7 +10,7 @@ - + diff --git a/XUnitTest/XUnitTest.csproj b/XUnitTest/XUnitTest.csproj index c6d331e..67b1804 100644 --- a/XUnitTest/XUnitTest.csproj +++ b/XUnitTest/XUnitTest.csproj @@ -12,7 +12,7 @@ - +