.net core基础入门

前言

从一个手写的.NET Core项目开始,了解 .Net Core的基础,再了解一些必须了解的基本理论,如依赖注入控制反转等,这样才能最直接的了解 .Net Core与原来 .Net Framwork有什么区别。

程序Program

说明

因为.Net Core是跨平台的,每一个ASP.NET Core 应用程序,从本质上来说,都是一个独立的控制台应用,并不需要依托IIS来运行,这也是它能实现跨平台的一个基础。

Program.cs类

Program.cs类的作用是创建并运行WebHost实例,调用Startup类,代码如下:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

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

CreateHostBuilder(args) 创建一个 IHostBuilder 对象,大概理解就是一个编译器

应用启动Startup

Startup类

Startup 类是 ASP.NET Core 应用程序启动默认调用的类,该类是在 Program.cs 中配置:

Startup 默认有两个重要的方法ConfigureServicesConfigure

  • ConfigureServices 配置应用所需服务,在该方法中可以注册应用所需要的功能或服务,并通过依赖关系注入 (DI) 或 ApplicationServices 在整个应用中使用服务
  • Configure 配置应用请求处理管道,如MVC,路由等

ConfigureServices

Configure 方法配置应用服务之前,由主机调用,对于需要大量设置的功能,IServiceCollection 上有 Add{Service} 扩展方法。 例如,AddDbContext、AddDefaultIdentity、AddEntityFrameworkStores 和 AddRazorPages

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
        options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}
注入模式
  • 单例(Singleton)在它们第一次被请求时创建(或者如果你在ConfigureServices运行时指定一个实例)并且每个后续请求将使用相同的实例。
  • 瞬时(Transient)在它们每次请求时都会被创建。这一生命周期适合轻量级的,无状态的服务。
  • 作用域(Scoped)在每次请求中只创建一次。
//单例注入,创建的对象在所有的地方所有的请求会话创建的都是相同
services.AddSingleton<IStudentRepository, MockStudentRepository>();

//瞬时注入,每次都创建一个新的对象
services.AddTransient<IStudentRepository, MockStudentRepository>();

//作用域注入,创建的对象在同一个请求会话时是相同的
services.AddScoped<IStudentRepository, MockStudentRepository>();

Configure

Configure 方法用于指定应用响应 HTTP 请求的方式。 可通过将中间件组件添加到 IApplicationBuilder 实例来配置请求管道。 Configure 方法可使用 IApplicationBuilder,但未在服务容器中注册。 托管创建 IApplicationBuilder 并将其直接传递到 Configure。

中间件

  1. 中间件可以同时被访问和请求
  2. 可以处理请求后,将请求传递给下一个中间件
  3. 可以处理请求后,使管道短路
  4. 可以处理传出响应
  5. 中间件是按照添加的顺序执行的
配置方法
  • Use方法可以使管道短路
  • Map扩展用作分支管道的约定,映射根据给定的请求路径的匹配来分支请求流水线,如果请求路径以给定路径开始,则执行分支
    • MapWhen基于给定谓词的结果创建请求管道分支。 Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。
  • Run方法是一个约定,如果将Run放在了Configure里面,它也是终端中间件。
    执行顺序
app.Use(async (context, next) =>
{
    context.Response.ContentType = "text/plain;charset=utf-8;"; //解决中文乱码
    await context.Response.WriteAsync("Use1之前");
    await next(); //调用下一个中间件
    await context.Response.WriteAsync("Use1之后");
});
app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Use2之前");
    await next(); //调用下一个中间件
    await context.Response.WriteAsync("Use2之后");
});
app.Run(async (context) =>
{
    //获取当前进程名
    await context.Response.WriteAsync( System.Diagnostics.Process.GetCurrentProcess().ProcessName);
});

请求过来后,以链式的方式执行: Use1之前 --> next --> Use2之前 --> next --> Run --> Use2之后 --> Use1之后

内置中间件
中间件描述使用顺序
身份验证提供身份验证支持app.UseAuthentication()在需要 HttpContext.User 之前。 OAuth 回叫的终端。
授权提供身份验证支持app.UseAuthorization()紧接在身份验证中间件之后。

多启动

应用为不同的环境(例如,StartupDevelopment)单独定义 Startup 类时,相应的 Startup 类会在运行时被选中。 优先考虑名称后缀与当前环境相匹配的类。

参考代码:

public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
    Configuration = configuration;
    _env = env;
}

public IConfiguration Configuration { get; }
private readonly IWebHostEnvironment _env;

public void ConfigureServices(IServiceCollection services)
{
    if (_env.IsDevelopment())
    {
        Console.WriteLine(_env.EnvironmentName);
    }
    else if (_env.IsStaging())
    {
        Console.WriteLine(_env.EnvironmentName);
    }
    else
    {
        Console.WriteLine("Not dev or staging");
    }

    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app)
{
    if (_env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

实际开发中,我们通过appsettings.{Environment}.json文件,如appsetting.json,appsetting.Devlopment.json等不同名称文件来配置不同环境配置,

依赖注入(DI)

ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖关系之间实现控制反转 (IoC) 的技术。

概述

依赖项是指另一个对象所依赖的对象。

示例

新建接口,实现WriteMessage方法

public interface IMyDependency
{
    void WriteMessage(string message);
}

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

在Startup类中ConfigureServices方法注入依赖

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();
    services.AddRazorPages();
}

当实际需要调用WriteMessage方法执行时,通过构造函数,注入IMyDependency接口

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

通过使用 DI 模式,表示控制器:

  • 不使用具体类型 MyDependency,仅使用它实现的 IMyDependency 接口。 这样可以轻松地更改控制器使用的实现,而无需修改控制器。
  • 不创建 MyDependency 的实例,这由 DI 容器创建。

当下次需要修改WriteMessage方法时

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

修改Startup类中ConfigureServices方法注入

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency2>();
    services.AddRazorPages();
}

这样就不需要修改业务代码

控制反转(IoC)

应用程序中的依赖关系方向应该是抽象的方向,而不是实现详细信息的方向。 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个直接依赖项关系图。 也就是说,如果类 A 调用类 B 的方法,类 B 调用 C 类的方法,则在编译时,类 A 将取决于类 B,而 B 类又取决于类 C,如图 4-1 所示。
image
应用依赖关系反转原则后,A 可以调用 B 实现的抽象上的方法,让 A 可以在运行时调用 B,而 B 又在编译时依赖于 A 控制的接口(因此,典型的编译时依赖项发生反转)。 运行时,程序执行的流程保持不变,但接口引入意味着可以轻松插入这些接口的不同实现

依赖项反转是生成松散耦合应用程序的关键一环,因为可以将实现详细信息编写为依赖并实现更高级别的抽象,而不是相反。 因此,生成的应用程序的可测试性、模块化程度以及可维护性更高。 遵循依赖关系反转原则可实现依赖关系注入。

简单理解,就是我们通过调用接口,然后通过反射,找到实现了接口的方法,执行。

EF Core

如何使用

EF Core 是一个 .NET Standard 2.0 库。 因此,EF Core 需要支持运行 .NET Standard 2.0 的实现。 其他 .NET Standard 2.0 库也可引用 EF Core

要将 EF Core 添加到应用程序,请安装适用于要使用的数据库提供程序的 NuGet 包。

数据库系统
SQL Server 和 SQL AzureMicrosoft.EntityFrameworkCore.SqlServer
SQLiteMicrosoft.EntityFrameworkCore.Sqlite
PostgreSQLNpgsql.EntityFrameworkCore.PostgreSQL*
MySQLPomelo.EntityFrameworkCore.MySql*
EF Core 内存中数据库**Microsoft.EntityFrameworkCore.InMemory

第一个EF Core程序

注入依赖

public void ConfigureServices(IServiceCollection services)
{
    // 配置数据库上下文,支持N个数据库
    //  services.AddDatabaseAccessor(options =>
    // {
        // 配置默认数据库
        // options.AddDbPool<SchoolContext>();
    // });
    
    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}

新建继承数据库上下文类

using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace EFGetStarted
{
    public class SchoolContext : DbContext
    {
        public SchoolContext (DbContextOptions<SchoolContext> options)
            : base(options)
        {
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }

    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

实际代码CURD


public class StudentModel : PageModel
{
    private readonly SchoolContext _context;
    private readonly ILogger<StudentModel> _logger;

    public StudentModel(ContosoUniversity.Data.SchoolContext context,
                       ILogger<StudentModel> logger) //依赖注入
    {
        _context = context;
        _logger = logger;
    }
    /// 查询
    public async Task<IActionResult> OnGetAsync(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
    
        // Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);
        Student = await _context.Students
            .Include(s => s.Enrollments)
            .ThenInclude(e => e.Course)
            .AsNoTracking()
            .FirstOrDefaultAsync(m => m.ID == id);
        if (Student == null)
        {
            return NotFound();
        }
        return Page();
    }
}
/// 更新
public async Task<IActionResult> OnPostAsync(int id)
{
    var studentToUpdate = await _context.Students.FindAsync(id);

    if (studentToUpdate == null)
    {
        return NotFound();
    }

    if (await TryUpdateModelAsync<Student>(
        studentToUpdate,
        "student",
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

注解说明

[Required] //验证必填
[Column("FirstName")]  //对应列名
[Display(Name = "First Name")]  //显示名称
[StringLength(50)]  //最大长度50
public string FirstMidName { get; set; }

[DataType(DataType.Date)] //数据格式为时间
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] //格式化时间格式
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; }

原理

EF Core 在查询数据时,

迁移

EF Core migrations add 命令用于创建数据库的代码,迁移代码位于 Migrations<timestamp>_InitialCreate.cs 文件中。 InitialCreate 类的 Up 方法创建与数据模型实体集对应的数据库表。 Down 方法删除这些表,如下例所示:

迁移说明

每次执行命令都会生成一个新的类名,类名前缀为时间戳,迁移会在 Migrations/SchoolContextModelSnapshot.cs 中创建当前数据模型的快照 。 添加迁移时,EF 会通过将当前数据模型与快照文件进行对比来确定已更改的内容。所以不能直接删除_.cs 文件来删除迁移,需要通过remove 命令来,重置快照,重新生成迁移脚本。

执行迁移命令后,会把对应的类型插入到数据库的__EFMigrationsHistory 表,代表已经执行,如果回滚,它会删除掉对应行记录

  • 10
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值