2021年01月30日 22:52
原创作品,转载时请务必以超链接形式标明文章原始出处,否则将追究法律责任。

image.png

不知不觉在科技二路已经上班长达十年了,从佳贝大厦到零一广场,太多的年头,一个年轻小伙熬成了大叔。年轻的时候什么都想折腾,什么都能折腾。现在感觉力不从心,电脑拿出来不知道自己要学什么,迷失了方向。

说这些废话也没啥用,不如写点博客来的实在,看一下我们的项目结构

image.png

关于这个EFCore链接SqlServer数据库在这我就不多说了,没啥说的。看项目结构就看得出来是工作单元模式,这个玩意我也不想多解释什么,截个图你细品。

image.png

依赖注入我们依然使用autofac,实体映射还是autoMapper,没啥说的。.net core 3.1版本,autofac需要这两步,在programe.cs中增加代码UseServiceProviderFactory

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

然后再Startup.cs中增加方法ConfigureContainer,再该方法中去设置注入的一些class。

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<MembershipManageContext>().As<DbContext>().InstancePerLifetimeScope();
    builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
    builder.RegisterType<CustomerRepository>().As<ICustomerRepository>().InstancePerLifetimeScope();
    builder.RegisterType<CustomerService>().As<ICustomerService>().InstancePerLifetimeScope();
}

ok,接下来我们看一下server端的api接口,创建customer

[ApiController]
[Route("[controller]")]
public class CustomerController : ControllerBase
{
    private readonly ICustomerService customerService;
    private readonly IMapper mapper;
    public CustomerController(
        ICustomerService customerService,
        IMapper mapper)
    {
        this.customerService = customerService;
        this.mapper = mapper;
    }
    [HttpGet("get")]
    public IEnumerable<Customer> GetCustomerList()
    {
        return customerService.GetCustomerList();
    }

    [HttpPost("create")]
    public void CreateCustomer(CustomerRequest customerRequest)
    {
        var customer = mapper.Map<Customer>(customerRequest);
        customerService.CreateCustomer(customer);
    }
}

这里我们注入了IMapper用于将前端提交的DTO实体转化为EFCore需要存储的DB对象。那这个mapper怎么知道映射规则呢?当然我们必须得自己写映射的规则了,安装AutoMapper.Extensions.Microsoft.DependencyInjection,然后我们新建一个class专门用于customer实体的映射规则。

public class CustomerProfile : Profile
{
    public CustomerProfile()
    {
        CreateMap<CustomerRequest, Customer>();
    }
}

这里的基类Profile是AutoMapper命名空间下的,只要是继承了该类,在依赖注入解析的时候,就会从所有继承了Profile类的设置中查找对应的设置,对实体进行转化。

好的,说了这么久,api是实现好了。我们看一下页面的实现,直接上代码

@page "/customer/create"
@using WebAssemblyDemo.Shared.Customer
@using WebAssemblyDemo.Server.Models
@inject HttpClient Http

<EditForm Model="@customer" OnValidSubmit="onValidSubmit" OnInvalidSubmit="onInvalidSubmit">
    <div id="customer" class="margin-t10">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">
                    <label>新增会员</label>
                </h3>
            </div>
            <div class="panel-content">
                <div class="row">
                    <div class="col-md-12">
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2"><span class="text-danger">*</span>用户名:</label>
                            <div class="col-md-10">
                                <input id="userNo" name="userNo" type="text" @bind-value="customer.UserNo" class="form-control" maxlength="11" autocomplete="off" placeholder="输入手机号" />
                                <ValidationMessage For="()=>customer.UserNo"></ValidationMessage>
                            </div>
                        </div>
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2"><span class="text-danger">*</span>密码:</label>
                            <div class="col-md-10">
                                <input id="userPwd" name="passWord" type="password" @bind-value="customer.Password" class="form-control" maxlength="10" />
                                <ValidationMessage For="()=>customer.Password"></ValidationMessage>
                            </div>
                        </div>
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2"><span class="text-danger">*</span>确认密码:</label>
                            <div class="col-md-10">
                                <input id="userRePwd" name="userRePwd" type="password" @bind-value="customer.RePassword" class="form-control" maxlength="10" />
                                <ValidationMessage For="()=>customer.RePassword"></ValidationMessage>
                            </div>
                        </div>
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2"><span class="text-danger">*</span>姓名:</label>
                            <div class="col-md-10">
                                <input id="name" name="name" type="text" class="form-control" @bind-value="customer.Name" maxlength="10" autocomplete="off" />
                                <ValidationMessage For="()=>customer.Name"></ValidationMessage>
                            </div>
                        </div>
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2"><span class="text-danger">*</span>性别:</label>
                            <div class="col-md-10">
                                <select id="userSex" name="sex" class="form-control" @bind="customer.Sex">
                                    <option value="">---请选择---</option>
                                    <option value="1">男</option>
                                    <option value="0">女</option>
                                </select>
                                <ValidationMessage For="()=>customer.Sex"></ValidationMessage>
                            </div>
                        </div>
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2">推荐人:</label>
                            <div class="col-md-10">
                                <InputSelect @bind-Value="customer.ParentID" class="form-control">
                                    @foreach (var customer in customerList)
                                    {
                                        <option value="@customer.Id">@customer.Name</option>
                                    }
                                </InputSelect>
                            </div>
                        </div>
                        <div class="form-group" style="height:35px">
                            <label class="col-md-2"></label>
                            <div class="col-md-10">
                                <button type="submit" class="btn btn-primary">保存</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <DataAnnotationsValidator />
            @*<ValidationSummary />*@
        </div>
    </div>
</EditForm>

@code {

    private List<Customer> customerList = new List<Customer>();
    private CustomerRequest customer = new CustomerRequest();
    private EditContext editContext;

    protected override async Task OnInitializedAsync()
    {
        editContext = new EditContext(customer);
        customerList = await Http.GetFromJsonAsync<List<Customer>>("customer/get");
    }

    private void onValidSubmit()
    {
        this.Http.PostAsJsonAsync<CustomerRequest>("customer/create", customer);
    }
    private void onInvalidSubmit()
    {
        
    }
}

在这里,所有表单都进行了双向绑定,使用@bind-value或者@bind。大家注意到必填项下面都有一个ValidationMessage元素,这个是框架提供的组件,用来显示验证信息。在这里只需要设置For="()=>customer.Sex",Lambda表达式中只需要写你需要验证的字段即可。另外大家注意到这里框架提供的InputSelect,除了这个组件外,还有如下的一些内置组件可供使用

image.png

大家自己研究即可。在组件的初始化完成以后,我们会实例化EditContext并传入该表单绑定的数据模型对象。然后因为我们的下拉列表需要数据源,所以发请求去请求api拿到customerList,注意在顶部,我们直接注入http实例,即可使用。这个页面我们定义的路由地址是customer/create,所以我们在浏览器输入这个地址,看一下页面运行的效果图

image.png

推荐人这一栏下拉列表已经加载上了从api请求的数据,验证消息也显示出来了,我们看一下验证怎么实现的,其实验证就是在绑定实体上打上验证标签,如下

public class CustomerRequest
{
    public int ID { get; set; }
    [Required(ErrorMessage = "用户名不能为空!")]
    public string UserNo { get; set; }

    [Required(ErrorMessage = "性别不能为空!")]
    public Nullable<int> Sex { get; set; }
    public Nullable<System.DateTime> InDate { get; set; }
    public string InUser { get; set; }
    [Required(ErrorMessage = "姓名不能为空!")]
    public string Name { get; set; }
    public int ParentID { get; set; }
    [Required(ErrorMessage = "密码不能为空!")]
    [StringLength(10, ErrorMessage = "密码长度必须介于6到10位!", MinimumLength = 6)]
    [RegularExpression("^[0-9a-zA-Z_]{1,}$", ErrorMessage = "密码必须由字母数组和下划线组成!")]
    public string Password { get; set; }
    [Required(ErrorMessage = "确认密码不能为空!")]
    [Compare("Password", ErrorMessage = "确认密码和密码不一致!")]
    public string RePassword { get; set; }
    public bool Status { get; set; }
}

ok,其实这个实现和asp.net mvc的实现有些类似。好的,最后我们再看一下提交,只有验证通过了我们才能请求api去创建customer,所以我们在onValidSubmit方法中去请求api创建customer。看下面的方法,很直接发送json格式数据去请求api

this.Http.PostAsJsonAsync<CustomerRequest>("customer/create", customer);

提交,直接进入断点,如下,没有问题

image.png

当然我们只验证客户端表单是远远不够的,我们还要在API端进行验证。我们在Swagger看一下

image.png

在这里userno为空,sex=-1,我们看一下验证效果

image.png

看到没,请求还未到达action,已经被半路拦截了。ok,今天就到这里,拜拜


发表评论
匿名  
用户评论

匿名游客

2021年02月07日 12:32
你这个写的非常有参考性,赞一个

匿名游客

2021年02月03日 12:54
这个源码有没有呢?我先借鉴一下