ASP.NET Web API +实体框架+ Microsoft SQL Server + Angular。第1部分





介绍



关于如何使用ASP.NET Core技术,实体框架,Microsoft SQL Server DBMS和Angular框架创建简单Web应用程序的短期课程。我们将通过Postman应用程序测试Web API



该课程包括以下几个部分:



  1. 使用ASP.NET Web API和实体框架核心构建Web API。
  2. 在Angular中实现用户界面。
  3. 向应用程序添加身份验证。
  4. 扩展应用程序模型并探索实体框架的其他功能。


第1部分。使用ASP.NET Web API和实体框架核心构建Web API



例如,我们将考虑待办事项清单的经典应用。若要开发应用程序,我将使用Visual Studio 2019(该过程在Visual Studio 2017中类似)。



项目创建



在Visual Studio中创建一个新的ASP.NET Core Web应用程序项目:







命名应用程序,并指定该项目的目录路径:







并选择API应用程序模板:







模型



让我们创建一个Models目录并将第一个TodoItem.cs类添加到新目录中,该目录的对象将描述应用程序中待办事项列表的一些任务:



public class TodoItem
{
    public int Id { get; set; }
    public string TaskDescription { get; set; }
    public bool IsComplete { get; set; }
}


我们将使用Sql Server作为DBMS,并且将通过Entity Framework Core进行对数据库的访问,首先,我们将通过内置的NuGet包管理器安装框架:







使用实体框架的一种方法是“代码优先”方法。该方法的本质在于,基于应用程序模型(在我们的示例中,该模型表示单个类-TodoItem.cs),形成了数据库的结构(表,主键,链接),所有这些工作都“在幕后”进行,并直接与我们不使用SQL。模型类的先决条件是存在主键字段;默认情况下,Entity Framework将在其名称中查找带有子字符串“ id”的整数字段,并基于该字段形成主键。您可以使用自定义属性或Fluent API的功能来覆盖此行为。



使用实体框架的主要组件是数据库上下文类,通过它可以实际访问表中的数据:



public class EFTodoDBContext : DbContext
{
    public EFTodoDBContext(DbContextOptions<EFTodoDBContext> options) : base(options) 
    { }
    public DbSet<TodoItem> TodoItems{ get; set; }
}


基类DbContext创建数据库上下文,并提供对实体框架功能的访问。



我们将使用SQL Server 2017 Express存储应用程序数据连接字符串存储在一个名为appsettings.json的JSON文件中:



{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.\\SQLEXPRESS;Database=Todo;Trusted_Connection=true"
  }
}


接下来,您需要通过将以下代码添加到ConfigureServices()方法来修改Startup.cs类:



services.AddDbContext<EFTodoDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));


AddDbContext()方法为EFTodoDBContext数据库上下文类配置由实体框架核心提供的服务。 AddDbContext()方法的参数是一个lambda表达式,该表达式接收一个用于为上下文类配置数据库的options对象。在这种情况下,将使用UseSqlServer()方法并指定连接字符串来配置数据库。



让我们定义在ITodoRepository界面中处理任务的基本操作:



 public interface ITodoRepository
 {
    IEnumerable<TodoItem> Get();
    TodoItem Get(int id);
    void Create(TodoItem item);
    void Update(TodoItem item);
    TodoItem Delete(int id);
 }


这个接口使我们不必考虑数据仓库的具体实现,也许我们还没有完全决定是选择DBMS还是ORM框架,但现在没有关系,描述数据访问的类将从该接口继承。

让我们实现一个存储库,如前所述,该存储库将从ITodoRepository继承并使用EFTodoDBContext作为数据源:



public class EFTodoRepository : ITodoRepository
{
    private EFTodoDBContext Context;
    public IEnumerable<TodoItem> Get()
    {
        return Context.TodoItems;
    }
    public TodoItem Get(int Id)
    {
        return Context.TodoItems.Find(Id);
    }
    public EFTodoRepository(EFTodoDBContext context)
    {
        Context = context;
    }
    public void Create(TodoItem item)
    {
        Context.TodoItems.Add(item);
        Context.SaveChanges();
    }
    public void Update(TodoItem updatedTodoItem)
    {
        TodoItem currentItem = Get(updatedTodoItem.Id);
        currentItem.IsComplete = updatedTodoItem.IsComplete;
        currentItem.TaskDescription = updatedTodoItem.TaskDescription;

        Context.TodoItems.Update(currentItem);
        Context.SaveChanges();
        }

    public TodoItem Delete(int Id)
    {
        TodoItem todoItem = Get(Id);

        if (todoItem != null)
        {
            Context.TodoItems.Remove(todoItem);
            Context.SaveChanges();
        }

        return todoItem;
    }    
}


控制者



控制器的实现将在下面描述,它对EFTodoDBContext的数据上下文一无所知,而仅在工作中使用ITodoRepository接口,该接口允许在不更改控制器的情况下更改数据源。亚当·弗里曼(Adam Freeman)在他的“专业人士的ASP.NET Core MVC实体框架核心2”一书中将这种方法称为“存储”模式。



控制器实现用于标准HTTP请求方法的处理程序:GET,POST,PUT,DELETE,这将更改TodoItem.cs类中描述的任务状态。



将TodoController.cs类添加到Controllers目录中,其中包含以下内容:



[Route("api/[controller]")]
public class TodoController : Controller
{
    ITodoRepository TodoRepository;

    public TodoController(ITodoRepository todoRepository)
    {
        TodoRepository = todoRepository;
    }

    [HttpGet(Name = "GetAllItems")]
    public IEnumerable<TodoItem> Get()
    {
        return TodoRepository.Get();
    }

    [HttpGet("{id}", Name = "GetTodoItem")]
    public IActionResult Get(int Id)
    {
        TodoItem todoItem = TodoRepository.Get(Id);

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

        return new ObjectResult(todoItem);
    }

    [HttpPost]
    public IActionResult Create([FromBody] TodoItem todoItem)
     {
        if (todoItem == null)
        {
            return BadRequest();
        }
        TodoRepository.Create(todoItem);
        return CreatedAtRoute("GetTodoItem", new { id = todoItem.Id }, todoItem);
    }

    [HttpPut("{id}")]
    public IActionResult Update(int Id, [FromBody] TodoItem updatedTodoItem)
    {
        if (updatedTodoItem == null || updatedTodoItem.Id != Id)
        {
            return BadRequest();
        }

        var todoItem = TodoRepository.Get(Id);
        if (todoItem == null)
        {
            return NotFound();
        }

        TodoRepository.Update(updatedTodoItem);
        return RedirectToRoute("GetAllItems");
    }

    [HttpDelete("{id}")]
    public IActionResult Delete(int Id)
    {
        var deletedTodoItem = TodoRepository.Delete(Id);

        if (deletedTodoItem == null)
        {
            return BadRequest();
        }

        return new ObjectResult(deletedTodoItem);
    }
 }


在类定义之前,有一个描述用于访问控制器的路由模板的属性:[Route(“ api / [controller]”)]。可以通过以下途径访问TodoController:https:// // <host ip>:<port> / api / todo。 [Controller]以小写形式指定控制器类的名称,省略了“ Controller”部分。



在TodoController中定义每个方法之前,必须先指定格式的特殊属性:[<HTTP方法>(“参数”,名称=“方法别名”))。该属性确定该方法将处理哪个HTTP请求,在请求URI中传递的参数以及可以重新发送请求的方法的别名。如果未指定属性,则默认情况下,MVC框架将尝试根据方法名称和请求中的指定参数在控制器中查找最合适的方法来处理请求,因此,如果未在TodoController控制器中然后在HTTP请求中为Get()方法指定属性,使用GET方法:https:// // <host ip>:<port> / api / todo,基础结构将定义控制器的Get()方法来处理请求。



在其构造函数中,控制器收到对ITodoRepository类型的对象的引用,但是到目前为止,MVC基础结构尚不知道在创建控制器时要替换哪个对象。我们需要创建一个可以唯一解决此依赖关系的服务,为此,我们将以下代码添加到ConfigureServices()方法中,从而对Startup.cs类进行一些更改:



services.AddTransient<ITodoRepository, EFTodoRepository>();


AddTransient <ITodoRepository,EFTodoRepository>()方法定义了一个服务,该服务在需要ITodoRepository类型的实例时(例如在控制器中)创建EFTodoRepository类的新实例。



Startup.cs类的完整代码:



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

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
        services.AddDbContext<EFTodoDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
        services.AddTransient<ITodoRepository, EFTodoRepository>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
 }


移居



为了使Entity Framework从模型生成数据库和表,您必须使用数据库迁移过程。迁移是一组命令,这些命令为数据库准备与实体框架一起使用。它们用于创建和同步数据库。可以在Package Manager控制台和Power Shell(Developer Power Shell)中执行命令。我们将使用包管理器控制台来与Entity Framework一起使用,我们需要安装Microsoft.EntityFrameworkCore.Tools包:







启动包管理器控制台并运行Add-Migration Initial命令











一个新目录将出现在项目中-迁移,将在其中执行Update-Database命令后在数据库中创建哪些对象的基础上存储迁移类:







Web API准备就绪,通过在本地IIS Express上运行应用程序,我们可以测试控制器的操作。



测试WebAPI



让我们在Postman中创建一个名为TodoWebAPI的新请求集合:







由于我们的数据库为空,所以我们首先测试一个新任务的创建。在控制器中,Create()方法负责创建任务,该任务将处理POST方法发送的HTTP请求,并在请求正文中包含JSON格式的序列化TodoItem对象。 Create()方法中todoItem参数之前的[FromBody]属性告诉MVC框架从请求正文中反序列化TodoItem对象,并将其作为参数传递给该方法。让我们在Postman中创建一个请求,该请求会将请求发送到webAPI以创建新任务:







成功创建任务后,Create()方法将请求重定向到别名为“ GetTodoItem”的Get()方法,并将新创建的任务的ID作为参数传递,结果我们将以JSON格式接收创建的任务对象以响应该请求。



通过使用PUT方法发送HTTP请求并在URI ID(https:// localhost:44370 / api / todo / 1)中指定一个已创建的对象,并在请求主体中传递JSON格式有所更改的对象,我们将在数据库中更改此对象:







使用带有GET方法且未指定参数的HTTP请求,我们将接收数据库中的所有对象:







使用DELETE方法并在URI中指定对象ID的HTTP请求(https://本地主机:44370 / api / todo / 2),将从数据库中删除该对象并返回JSON远程任务:







就是这样,在下一部分中,我们将使用Angular JavaScript框架实现用户界面。



All Articles