使用 C# 和 Blazor 进行全栈开发

Blazor 是将 C# 引入浏览器的 Microsoft 试验框架,正好可以填补欠缺的 C# 一环。如今,C# 程序员可以编写桌面、服务器端 Web、云、电话、平板电脑、手表、电视和 IoT 应用程序。


Blazor 填补了欠缺的一环,C# 开发人员现在可以直接在用户浏览器中共享代码和业务逻辑。对于 C# 开发人员来说,这是一项十分强大的功能,可显著提升工作效率。


本文将展示常见的代码共享用例。我将展示如何在 Blazor 客户端和 WebAPI 服务器应用程序之间共享验证逻辑。目前,你不仅要在服务器中验证输入,还要在客户端浏览器中验证输入。新式 Web 应用程序的用户希望获得准实时反馈。在填写长窗体并单击“提交”后仅看到红色错误返回的日子已经一去不复返了。


在浏览器中运行的 Blazor Web 应用程序可以与 C# 后端服务器共享代码。可以将逻辑放入共享库中,并在前端和后端使用它。这会带来很多好处。


可以将所有规则都集中放置在一处,并知道只需在一处更新它们。它们的工作方式确实相同,因为它们是相同的代码。


在客户端和服务器逻辑并不总是完全相同的情况下,可以节省大量测试和故障排除时间。


也许最值得一提的是,可以在客户端和服务器上使用一个库进行验证。


以前,JavaScript 前端强制开发人员编写两个版本的验证规则:一个是用适用于前端的 JavaScript 编写,另一个是用适用于后端的语言编写。若要尝试解决这种不匹配问题,需要涉及复杂的规则框架和额外的抽象层。使用 Blazor,可以在客户端和服务器上运行同一.NET Core 库。


虽然 Blazor 仍是试验框架,但它的进展迅速。生成此示例前,请先确保已安装正确版本的 Visual Studio、.NET Core SDK 和 Blazor 语言服务。有关入门步骤,请访问 blazor.net。

新建 Blazor 应用程序

首先,新建 Blazor 应用程序。


在“新建项目”对话框中,依次单击“ASP.NET Core Web 应用程序”和“确定”,再选择图 1 所示对话框中的“Blazor”图标。单击“确定”。


这会创建默认的 Blazor 示例应用程序。如果已试用过 Blazer,便会对此默认应用程序很熟悉。


640?wx_fmt=png

图 1:选择 Blazor 应用程序



新的注册窗体将展示验证业务规则的共享逻辑。图 2 展示了包含“名字”、“姓氏”、“电子邮件地址”和“电话”字段的简单窗体。


在此示例中,它会验证所有字段是否都为必填、姓名字段是否有长度上限,以及电子邮件地址和电话字段的格式是否正确。它会在每个字段下显示错误消息,这些消息会在用户键入内容的同时更新。最后,只有在没有错误的情况下,“注册”按钮才处于启用状态。


640?wx_fmt=png

图 2:注册窗体

共享库

所有需要在服务器和 Blazor 客户端之间共享的代码都位于一个独立的共享库项目中。共享库包含模型类和非常简单的验证引擎。模型类保留注册窗体中的数据字段。该命令如下所示:


public class RegistrationData : ModelBase
{
[RequiredRule]
[MaxLengthRule(50)]
public String FirstName { get; set; }
[RequiredRule]
[MaxLengthRule(50)]
public String LastName { get; set; }
[EmailRule]
public String Email { get; set; }
[PhoneRule]
public String Phone { get; set; }
}



RegistrationData 类继承自 ModelBase 类,后者包含所有可用于验证规则并返回绑定到 Blazor 页面的错误消息的逻辑。每个字段都使用映射到验证规则的属性进行修饰。我选择了创建非常简单的模型,它很像实体框架 (EF) 数据注释模型。此模型的所有逻辑都包含在共享库中。


ModelBase 类包含 Blazor 客户端应用程序或服务器应用程序可用来确定是否有任何验证错误的方法。它还会在此模型更改时触发事件,以便客户端能够更新 UI。任何模型类都可以继承自它,并自动获取所有验证引擎逻辑。


首先,我将在 SharedLibrary 项目中新建 ModelBase 类,如下所示:


public class ModelBase
{
}

错误和规则

现在,我将向 ModelBase 类添加包含验证错误列表的专用字典。_errors 字典先以字段名称为键,再以规则名称为键。值是要显示的实际错误消息。


通过此设置,可以轻松确定特定字段是否有验证错误,并快速检索错误消息。


代码如下:


private Dictionary<String, Dictionary<String, String>> _errors =
new Dictionary<string, Dictionary<string, string>>();



现在,我将添加 AddError 方法,以将错误输入内部错误字典。


AddError 有 fieldName、ruleName 和 errorText 参数。它先搜索内部错误字典,并删除已有条目,再添加新的错误条目,如下面的代码所示:


private void AddError(String fieldName, String ruleName, String errorText)
{
if (!_errors.ContainsKey(fieldName)) { _errors.Add(fieldName,
new Dictionary<string, string>()); }
if (_errors[fieldName].ContainsKey(ruleName))
{ _errors[fieldName].Remove(ruleName); }
_errors[fieldName].Add(ruleName, errorText);
OnModelChanged();
}



最后,我将添加 RemoveError 方法,它接受 fieldName 和 ruleName 参数,并在内部错误字典中搜索并删除匹配的错误。代码如下:


private void RemoveError(String fieldName, String ruleName)
{
if (!_errors.ContainsKey(fieldName)) { _errors.Add(fieldName,
new Dictionary<string, string>()); }
if (_errors[fieldName].ContainsKey(ruleName))
{ _errors[fieldName].Remove(ruleName);
OnModelChanged();
}
}



下一步是添加 CheckRules 函数,这些函数负责查找并执行附加到此模型的验证规则。有两种不同的 CheckRules 函数:一种是缺少参数,但对所有字段验证全部规则;另一种有 fieldName 参数,并仅验证特定字段。在字段更新时,使用的是第二种函数,并立即对此字段验证规则。


CheckRules 函数使用反射来查找附加到字段的属性列表。然后,它测试每个属性,以确定属性类型是否为 IModelRule。找到 IModelRule 后,它调用 Validate 方法,并返回结果,如图 3 所示。


图 3:CheckRules 函数


public void CheckRules(String fieldName)
{
var propertyInfo = this.GetType().GetProperty(fieldName);
var attrInfos = propertyInfo.GetCustomAttributes(true);
foreach (var attrInfo in attrInfos)
{
if (attrInfo is IModelRule modelrule)
{
var value = propertyInfo.GetValue(this);
var result = modelrule.Validate(fieldName, value);
if (result.IsValid)
{
RemoveError(fieldName, attrInfo.GetType().Name);
}
else
{
AddError(fieldName, attrInfo.GetType().Name, result.Message);
}
}
}
}
public bool CheckRules()
{
foreach (var propInfo in this.GetType().GetProperties(
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance))
CheckRules(propInfo.Name);
return HasErrors();
}



接下来,我将添加 Errors 函数。此函数需要使用 fieldname 参数,并返回包含相应字段的错误列表的字符串。它使用内部 _errors 字典来确定相应字段是否有任何错误,如下所示:


public String Errors(String fieldName)
{
if (!_errors.ContainsKey(fieldName)) { _errors.Add(fieldName,
new Dictionary<string, string>()); }
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (var value in _errors[fieldName].Values)
sb.AppendLine(value);
return sb.ToString();
}



现在,我需要添加 HasErrors 函数,它会在此模型的任意字段有任何错误时返回 true。客户端使用此方法来确定是否应启用“注册”按钮。


另外,WebAPI 服务器也使用此方法来确定传入的模型数据是否有错误。此函数的代码如下:


public bool HasErrors()
{
foreach (var key in _errors.Keys)
if (_errors[key].Keys.Count > 0) { return true; }
return false;
}

值和事件

是时候添加 GetValue 方法了,它需要使用 fieldname 参数,并使用反射来查找此模型中的字段并返回字段值。Blazor 客户端使用此方法来检索当前值,并在输入框中显示它,如下所示:


public String GetValue(String fieldName)
{
var propertyInfo = this.GetType().GetProperty(fieldName);
var value = propertyInfo.GetValue(this);
if (value != null) { return value.ToString(); }
return String.Empty;
}



现在,添加 SetValue 方法。它使用反射来查找此模型中的字段,并更新字段值。然后,它触发 CheckRules 方法,以对相应字段验证所有规则。Blazor 客户端使用此方法,以在用户在输入文本框中键入内容的同时更新值。代码如下:


public void SetValue(String fieldName, object value)
{
var propertyInfo = this.GetType().GetProperty(fieldName);
propertyInfo.SetValue(this, value);
CheckRules(fieldName);
}



最后,我添加 ModelChanged 事件。如果此模型中的值已更改或在内部错误字典中添加或删除了验证规则,便会触发这个事件。Blazor 客户端侦听此事件,并在事件触发时更新 UI。正因为此,显示的错误会更新,如下面的代码所示:


public event EventHandler<EventArgs> ModelChanged;
protected void OnModelChanged()
{
  ModelChanged?.Invoke(this, new EventArgs());
}



得承认此验证引擎的设计非常简单,还有很多改进机会。在生产业务应用程序中,设置错误的严重性级别(如“信息”、“警告”和“错误”)会很有用。在某些情况下,如果无需修改代码,即可从配置文件动态加载规则,将会很有帮助。我不是在提倡创建你自己的验证引擎;只是有很多选择。此验证引擎既要足够好,以便演示实际示例;又要足够简单,以适应本文且易于理解。

创建规则

此时,有包含窗体字段的 RegistrationData 类。


此类中的字段使用 RequiredRule 和 EmailRule 等属性进行修饰。


RegistrationData 类继承自 ModelBase 类,后者包含所有用于验证规则并向客户端通知更改的逻辑。验证引擎的最后一部分是规则逻辑本身。接下来,我将对此进行探索。


首先,我在 SharedLibrary 中新建 IModelRule 类。


此规则由一个返回 ValidationResult 的 Validate 方法组成。每个规则都必须实现 IModelRule 接口,如下所示:


public interface IModelRule
{
ValidationResult Validate(String fieldName, object fieldValue);
}



接下来,我在 SharedLibrary 中新建 ValidationResult 类,它由两个字段组成。IsValid 字段指明规则是否有效,而 Message 字段则包含要在规则无效时显示的错误消息。代码如下所示:


public class ValidationResult
{
public bool IsValid { get; set; }
public String Message { get; set; }
}



示例应用程序使用四个不同的规则,所有规则都是继承自 Attribute 类并实现 IModelRule 接口的公共类。


现在,是时候创建规则了。请注意,所有验证规则都只是继承自 Attribute 类并实现 IModelRule 接口的 Validate 方法的类。如果输入的文本超过指定的长度上限,图 4 中的长度上限规则返回错误。其他用于验证必填字段、电话和电子邮件地址字段格式的规则的工作方式类似,区别在于它们对要验证的数据类型采用不同的逻辑。


图 4:MaxLengthRule 类


public class MaxLengthRule : Attribute, IModelRule
{
private int _maxLength = 0;
public MaxLengthRule(int maxLength) { _maxLength = maxLength; }
public ValidationResult Validate(string fieldName, object fieldValue)
{
var message = $"Cannot be longer than {_maxLength} characters";
if (fieldValue == null) { return new ValidationResult() { IsValid = true }; }
var stringvalue = fieldValue.ToString();
if (stringvalue.Length > _maxLength )
{
return new ValidationResult() { IsValid = false, Message = message };
}
else
{
return new ValidationResult() { IsValid = true };
}
}
}

创建 Blazor 注册窗体

至此,验证引擎已在共享库中完成,它可以应用于 Blazor 应用程序中的新注册窗体。首先,我在 Blazor 应用程序中添加对共享库项目的引用。为此,可使用“引用管理器”对话框的“解决方案”窗口,如图 5 所示。


640?wx_fmt=png

图 5:添加对共享库的引用


接下来,我向应用程序的 NavMenu 添加新导航链接。

打开SharedNavMenu.cshtml 文件,并向列表添加新注册窗体链接如图 6 所示。


图 6:添加注册窗体链接


<div class=@(collapseNavMenu ? "collapse" : null) onclick=@ToggleNavMenu>
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match=NavLinkMatch.All>
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="registrationform">
<span class="oi oi-list-rich" aria-hidden="true"></span> Registration Form
</NavLink>
</li>
</ul>
</div>



最后,我在 Pages 文件夹中添加新 RegistrationForm.cshtml 文件。为此,可使用图 7 中的代码。


图 7 中的 cshtml 代码在 <form> 标记内有四个 <TextInput> 字段。<TextInput> 标记是自定义 Blazor 组件,用于处理字段的数据绑定和错误显示逻辑。此组件只需要三个参数即可正常运行:


  • Model 字段:标识数据要绑定到的类。

  • FieldName:标识数据要绑定到的数据成员。

  • DisplayName 字段:让组件可以显示易记消息。


图 7:添加 RegistrationForm.cshtml 文件


@page "/registrationform"
@inject HttpClient Http
@using SharedLibrary
<h1>Registration Form</h1>
@if (!registrationComplete)
{
<form>
<div class="form-group">
<TextInput Model="model" FieldName="FirstName" DisplayName="First Name" />
</div>
<div class="form-group">

<TextInput Model="model" FieldName="LastName" DisplayName="Last Name" />

</div>
<div class="form-group">
<TextInput Model="model" FieldName="Email" DisplayName="Email" />
</div>
<div class="form-group">
<TextInput Model="model" FieldName="Phone" DisplayName="Phone" />
</div>
<button type="button" class="btn btn-primary" onclick="@Register"
disabled="@model.HasErrors()">Register</button>
</form>
}
else
{
  <h2>Registration Complete!</h2>
}
@functions {
bool registrationComplete = false;
RegistrationData model { get; set; }
protected override void OnInit()
{
base.OnInit();
model = new RegistrationData() { FirstName =
"test", LastName = "test", Email = "test@test.com", Phone = "1234567890" };
model.ModelChanged += ModelChanged;
model.CheckRules();
}
private void ModelChanged(object sender, EventArgs e)
{
base.StateHasChanged();
}
async Task Register()
{
await Http.PostJsonAsync<RegistrationData>(
"https://localhost:44332/api/Registration", model);
registrationComplete = true;
}
}



在页面的 @functions 块内,代码只有一点点。OnInit 方法使用其中的一些测试数据来初始化模型类。


它绑定到 ModelChanged 事件,并调用 CheckRules 方法来验证规则。


ModelChanged 处理程序调用 base.StateHasChanged 方法,以强制执行 UI 刷新。Register 方法在“注册”按钮获得单击时调用,并将注册数据发送到后端WebAPI 服务。


TextInput 组件包含输入标签、输入文本框、验证错误消息,以及在用户键入内容的同时更新模型的逻辑。Blazor 组件非常易于编写,并提供了将接口分解为可重用部分的强大方法。参数成员使用 Parameter 属性进行修饰,以便让 Blazor 知道它们是组件参数。


输入文本框的 oninput 事件连接到 OnFieldChanged 处理程序。每当输入更改,都会触发此事件。然后,OnFieldChanged 处理程序调用 SetValue 方法,以对相应字段执行规则,并在用户键入内容的同时实时更新错误消息。图 8 展示了代码。


图 8:更新错误消息


@using SharedLibrary
<label>@DisplayName</label>
<input type="text" class="form-control" placeholder="@DisplayName"
oninput="@(e => OnFieldChanged(e.Value))"
value="@Model.GetValue(FieldName)" />
<small class="form-text" style="color:darkred;">@Model.Errors(FieldName)
</small>
@functions {
[Parameter]
ModelBase Model { get; set; }
[Parameter]
String FieldName { get; set; }
[Parameter]
String DisplayName { get; set; }
public void OnFieldChanged(object value)
{
Model.SetValue(FieldName, value);
}
}

服务器上的验证

验证引擎现已开始在客户端上运行。下一步是在服务器上使用共享库和验证引擎。为此,我先向解决方案添加另一个 ASP.NET Core Web 应用程序项目。这次,我在图 1 所示的“新建 ASP.NET Core Web 应用程序”对话框中选择的是“API”,而不是“Blazor”。


新建 API 项目后,我就添加对共享项目的引用,就像在 Blazor 客户端应用程序中(见图 5)一样。接下来,我向 API 项目添加新控制器。


新控制器接受来自 Blazor 客户端的 RegistrationData 调用,如图 9 所示。注册控制器在服务器上运行,并且是后端 API 服务器的典型特征。区别在于,它现在运行在客户端上运行的相同验证规则。


图 9:注册控制器


[Route("api/Registration")]
[ApiController]
public class RegistrationController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] RegistrationData value)
{
if (value.HasErrors())
{
return BadRequest();
}
// TODO: Save data to database
return Created("api/registration", value);
}
}



注册控制器有一个 POST 方法,它接受 RegistrationData 作为自己的值。它调用 HasErrors 方法,以验证所有规则并返回布尔值。


若有错误,控制器返回 BadRequest 响应;否则,它返回成功响应。我特意省略掉了将注册数据保存到数据库的代码,这样我就可以验证方案为重点了。


现在,共享验证逻辑在客户端和服务器上运行。

此简单示例展示了如何在浏览器和后端之间共享验证逻辑,仅仅触及全栈 C# 环境强大功能的皮毛。


Blazor 的神奇之处在于,使用它,现有 C# 开发人员大军可以生成功能强大的新式响应式单页应用程序,且最大限度地缩短启动时间。使用它,企业可以重用和重新打包现有代码,以便能够直接在浏览器中运行现有代码。


能够在浏览器、桌面、服务器、云和移动平台之间共享 C# 代码,将大大提升开发人员的工作效率。它还便于开发人员更快地向客户交付更多功能和更多业务价值。

原文地址:https://msdn.microsoft.com/zh-cn/magazine/mt833288

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 
640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316243.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AWS vs K8s 是新的 Windows vs Linux

作者&#xff1a;Ian Miell是开源程序员、演讲师、作家和博客写手以前……如果你与我一样&#xff0c;年过四十&#xff0c;又在IT行业工作&#xff0c;恐怕还记得每个人使用Windows&#xff0c;一小群但越来越多的人在业余时间埋头编译Linux的年代。Windows用户见此情形会困惑…

Asp.Net Core中的静态文件-12

目录本文出自《从零开始学 ASP.NET CORE MVC》目录 推荐文章&#xff1a;配置 ASP.NET Core 请求(Request)处理管道Asp.Net Core 中的静态文件在这个视频中我们将讨论如何使 ASP.NET Core 应用程序&#xff0c;支持静态文件&#xff0c;如 HTML&#xff0c;图像&#xff0c;CSS…

在.net core 中PetaPoco结合EntityFrameworkCore使用codefirst方法进行开发

在.net core开发过程中&#xff0c;使用最多的就是注入方法。但是在.net core使用PetaPoco时&#xff0c;PetaPoco还不支持进行注入方式进行处理一些问题。今天对PetaPoco进行了一些扩展&#xff0c;可以很方便的将PetaPoco进行注入操作&#xff0c;使用和EF很相似&#xff0c;…

F-Pairwise Modulo

d数组是来算&#xff08;x整除y&#xff09;*y中y比x小的数 s数组是算&#xff08;x整除y&#xff09;*y中y比x大的数 &#xff08;x整除y&#xff09;*y 看x对于前面大于他的数是枚举&#xff0c;对于前面小于他的数是d树状数组储存。 d中 x整除y表示x中有多少个y 所以 …

【微服务学习】Polly:熔断降级组件

何为熔断降级“熔断器如同电力过载保护器。它可以实现快速失败&#xff0c;如果它在一段时间内侦测到许多类似的错误&#xff0c;会强迫其以后的多个调用快速失败&#xff0c;不再访问远程服务器&#xff0c;从而防止应用程序不断地尝试执行可能会失败的操作&#xff0c;使得应…

A - Junk-Mail Filter HDU - 2473

只是这样合并&#xff0c;分割点的时候就不能分了。 这样合并再加上虚拟节点&#xff0c;那么每个你要求的节点就的下面就不连其他节点了&#xff0c;这样就可以进行删除操作了 #include<iostream> #include<algorithm> #include<cstdio> #include<cstr…

为什么不要使用 async void

问题在使用 Abp 框架的后台作业时&#xff0c;当后台作业抛出异常&#xff0c;会导致整个程序崩溃。在 Abp 框架的底层执行后台作业的时候&#xff0c;有 try/catch 语句块用来捕获后台任务执行时的异常&#xff0c;但是在这里没有生效。原始代码如下&#xff1a;public class …

张队长主讲这堂 .NET Core技术培训公开课,太原你约不约

这堂.NET Core技术培训课&#xff0c;你不能错过各位开发者朋友们想必也能体会到&#xff0c;现在市面上关于.NET Core的培训课程少之又少&#xff0c;其中有质量有内容的课程更是凤毛麟角&#xff0c;良师难遇&#xff0c;一课难求。但是现在&#xff0c;机会来了。中微云孵邀…

微软推出新语言Bosque,超越结构化程序设计

微软近期推出了一款全新的编程语言 Bosque&#xff0c;该语言参考了 TypeScript 的语法与类型&#xff0c;还有 ML 和 Node/JavaScript 的语义。作者微软计算机科学家 Mark Marron 致力于消除编程过程中出现的各种复杂情况&#xff0c;创造出了他认为超越主流结构化程序设计的 …

vue 实验报告8 基于Nuxt.js开发一个Vue程序,实现登录和注册切换

一、步骤&#xff1a; 保证node.js版本在14以上 1. 全局安装create-nuxt-app: npm install -g create-nuxt-app2.9.x 2. 创建项目: create-nuxt-app my-nuxt-demo 选项这么选&#xff1a; 然后输入&#xff1a; cd my-nuxt-demo 3. 创建登录和注册页面: 在/pages目录下创建logi…

解决vs2019中暂时无法为.net core WinForms使用 Designer 的临时方法

以下方法来自于微软github开源项目WinForms:dotnet/winforms - Using the Classic WinForms Designer in WinForms Core, 请放心使用 .目前.net core下的 Windows Forms的可视化设计器(Designer)尚不可用&#xff0c;后续的Visual Studio 2019 Update才会支持该部分的功能。不过…

P2480 [SDOI2010]古代猪文(数论好题)

P2480 [SDOI2010]古代猪文 题意&#xff1a; 给你n和g&#xff0c;求g∑d∣nCndmodpg^{\sum_{d|n}C_{n}^{d}}\bmod pg∑d∣n​Cnd​modp p999911659 题解&#xff1a; 这个一个综合性很强的数论题 涉及到欧拉定理&#xff0c;Lucas定理&#xff0c;中国剩余定理&#xff0c…

ASP.NET Core开发者成长路线图

来源: MoienTajik/AspNetCore-Developer-Roadmap.2019年ASP.NET Core开发者指南:你可以在下面找到一张图&#xff0c;该图展示了你可以选取的路径及你想学习的库&#xff0c;从而成为一名 ASP.NET Core 开发者。“作为 ASP.NET Core 开发者&#xff0c;我接下来应该学习什么&am…

.NET Framework VS .NET Core

本文对应的原文来至 c-sharpcorner 的一篇文章&#xff0c;文末有链接。如有错误&#xff0c;还请指正。前言你会为你的下一个应用程序选择哪一种开发平台 - .NET Framework 或者 .NET Core&#xff1f;在这篇文章中&#xff0c;让我们比较一下这两个开发平台的特点&#xff0c…

解决 VS2019 中.net core WPF 暂时无法使用 Designer 的临时方法

以下方法来自于微软github开源项目WPF:dotnet/samples - WPF Hello World sample with linked files&#xff0c;请放心使用。此篇文章是上篇文章解决vs2019中暂时无法为.net core WinForms使用 Designer 的临时方法的姊妹篇&#xff0c;但对WPF而言实现起来比WinForms简单很多…

.NET Core 迁移躺坑记

最近将自己负责的一个核心接口系统从.Net Framework迁移到了.Net Core。整体过程&#xff0c;从业务层面说一般般吧(整体还好但还是搞的业务有感&#xff0c;没出严重故障&#xff09;但是技术层面上感觉其实并没有达到要求&#xff0c;不过预期也是应该不会那么顺利&#xff0…

[Abp vNext 源码分析] - 2. 模块系统的变化

一、简要说明本篇文章主要分析 Abp vNext 当中的模块系统&#xff0c;从类型构造层面上来看&#xff0c;Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块&#xff0c;它现在则是 IModuleManager 和 IModuleLoader 来协同工作&#xff0c;其他的代码逻辑并…

P3301 [SDOI2013]方程

P3301 [SDOI2013]方程 题意&#xff1a; 题解&#xff1a; 插板法介绍 首先要先讲组合数学的一个方法&#xff1a;插板法 问题引出&#xff1a;把10个球放进三个盒子&#xff0c;每个箱子至少一个有多少种分法&#xff1f; 10个球就有9个空隙&#xff0c;我们可以考虑在这个…

.NET Framework 4.8发布

原文地址&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-the-net-framework-4-8/我们很高兴地宣布今天发布.NET Framework 4.8。它包含在Windows 10 May 2019更新中。.NET Framework 4.8也可在Windows 7和Windows Server 2008 R2 上使用。您可以从我们的 .NET下…

[NewLife.XCode]数据层缓存(网站性能翻10倍)

NewLife.XCode是一个有10多年历史的开源数据中间件&#xff0c;支持nfx/netcore&#xff0c;由新生命团队(2002~2019)开发完成并维护至今&#xff0c;以下简称XCode。整个系列教程会大量结合示例代码和运行日志来进行深入分析&#xff0c;蕴含多年开发经验于其中&#xff0c;代…