我是.NET Core 2.1 MVC的新手,无法理解某些功能的工作原理

时间:2019-11-22 21:42:10

标签: asp.net asp.net-core asp.net-web-api asp.net-core-mvc asp.net-core-2.1

我目前正在关注Udemy中的.Net Core Angular 8教程。我可以在Postman中获取/发布请求,还可以使用sqlite作为数据库查看.db文件中发布的内容,并通过Db浏览器查看数据。一切似乎都很好,但是如果我不理解应用程序某些区域的情况,那一切都将一事无成。如果有人可以帮助我回答几个问题,我将不胜感激。

我的整个项目都在GitHub:https://github.com/cjtejada/ASP.NetCoreAngular8/tree/master/DatingApp.API

问题1:我具有以下控制器:

    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {

        private readonly IAuthRepository _repo;
        private readonly IConfiguration _config;

        public AuthController(IAuthRepository repo, IConfiguration config)
        {
            _repo = repo;
            _config = config;
        }

        [HttpPost("register")]
        public async Task<IActionResult> Register(UserForRegisterDto userForRegisterDto)
        {
            // validate request

            userForRegisterDto.Username = userForRegisterDto.Username.ToLower();

            if (await _repo.UserExists(userForRegisterDto.Username))
                return BadRequest("User already exists");

            var userToCreate = new User
            {
                Username = userForRegisterDto.Username
            };

            var createdUser = await _repo.Register(userToCreate, userForRegisterDto.Password);

            return StatusCode(201);
        }
    }

我知道,当客户端发出注册请求时,将调用register()方法,传入的用户名将从DTO userForRegisterDto设置用户名。之后,我们调用方法UserExists()来检查用户是否存在于我们的数据库中。

问题1: 仅使用接口IAuthRepository时,_repo如何知道方法UserExists()中的逻辑?我知道IAuthRepository和AuthRepository类以某种方式链接在一起,但在应用程序中未发生构造器DI的任何地方。我的怀疑是它与configure.services方法下的startup.cs中的这一行有关:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        services.AddCors();
        services.AddScoped<IAuthRepository, AuthRepository>(); //<---- This Line
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });
    }

将这两个“链接起来”后,便可以通过AuthRepository类访问UserExists()方法:

public class AuthRepository : IAuthRepository
{
    private readonly DataContext _context;
    public AuthRepository(DataContext context)
    {
        _context = context;
    }

    public async Task<User> Login(string username, string password)
    {

    }

    private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
    {

    }

    public async Task<User> Register(User user, string password)
    {
        byte[] passwordHash, passwordSalt;
        CreatePasswordHash(password, out passwordHash, out passwordSalt);

        user.PasswordHash = passwordHash;
        user.PasswordSalt = passwordSalt;

        await _context.Users.AddAsync(user);
        await _context.SaveChangesAsync();

        return user;
    }

    private void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
    {


    }

    public async Task<bool> UserExists(string username)
    {
        if (await _context.Users.AnyAsync(x => x.Username == username))
            return true;

        return false;
    }
}

我一直在阅读有关AddScoped方法及其作用的信息,但是我不清楚这是事实。任何关于它如何工作的澄清都很好。

问题2: 这个差不多是一样的。如果我们继续遵循请求的路径,我们将点击AuthRepository类中的register()方法。

问题2: 当我也无法在任何地方发现构造函数DI的任何实例时,此类如何访问DataContext _context的属性?

如果需要,这里是我的其余项目文件:

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        services.AddCors();
        services.AddScoped<IAuthRepository, AuthRepository>();
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            //app.UseHsts();
        }

        //app.UseHttpsRedirection();
        app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
        app.UseAuthentication();
        app.UseMvc();
    }
}

DataContext.cs

public class DataContext : DbContext
{
    public DataContext(DbContextOptions<DataContext> options) : base (options){}
    public DbSet<Value> Values { get; set; }
    public DbSet<User> Users { get; set; }
}

任何澄清和建议,我们将不胜感激。谢谢,一切。

2 个答案:

答案 0 :(得分:2)

  1. 您是正确的。行services.AddScoped<IAuthRepository, AuthRepository>();仅指示ASP.NET Core服务容器在运行时看到对AuthRepository的引用时替换具体类IAuthRepository的实例。

    关于注册接口=>类的映射,各种Add*方法都在做同样的事情,关键的区别是创建的类的scope,即它可以持续多久:

    • AddScoped类将在对服务器的每个请求的开头创建,并在每个请求的结尾销毁。换句话说,每个请求都会导致创建该类的新实例。
    • AddSingleton类在ASP.NET Core应用程序启动时创建,并在关闭时销毁。换句话说,您的应用程序中仅存在该类的单个实例。
    • 只要请求,就会重新创建
    • AddTransient类。换句话说,如果您网站上的页面两次使用相同的服务瞬态,则将创建两个实例。 (将其与作用域服务进行对比,该作用域服务将仅创建一个实例,因为每个页面都是一个请求。)


    更完整的解释,包括示例:https://stackoverflow.com/a/38139500/70345

  2. 为了通过创建类AuthRepository的实例来实现(1),服务容器需要调用该类的构造函数。容器检查您的类以找到第一个公共构造函数,并检索该构造函数的所有参数,在本例中为DataContext类的实例。然后,容器在其内部类映射中搜索该类,并且由于您已经通过services.AddDbContext<DataContext>(...)注册了该映射,因此该容器能够构造并返回该类实例。因此,它能够将该实例传递给AuthRepository,因此AuthRepository被成功构造。

    AddDbContext方法只是AddScoped的包装,它执行了一些附加的脚手架以使实体框架DbContext能够正常工作。

有关官方解释,refer to Microsoft's official page on DI and IoC

答案 1 :(得分:1)

问题1-您已在Startup.cs中的此行右边提供了创建新对象AuthRepository的权限。对于此示例,您必须知道DI容器基于接口及其自己的实现为您创建AuthRepository对象,并且只需要在适当的构造函数中传递接口即可。 AddScope()与创建的对象的生存期相关。通过方法AddScope()注册对象时,将为单个请求创建对象,并且在请求之后将处理该对象。

问题2-您的dbContext已在DI容器中注册。 AddDbContext()是提供给实体框架dbContextes注册的特定扩展方法。此行代码使用从appSetting.json文件获取的连接字符串注册dbContext。

services.AddDbContext<DataContext>(x => 
   x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

此DbContext注入到AuthRepository类的构造函数中,并且当您使用此类DI容器为您创建的DbContext实例时。

private readonly DataContext _context;

public AuthRepository(DataContext context)
{
    _context = context;
}