《巧用.NET Core WebApi 实现字段扩展处理程序》

一、.NET Core WebApi 基础架构


.NET Core WebApi 的基础架构由多个关键部分组成,这些部分协同工作,为开发高效、可扩展的 Web 应用程序提供了坚实的基础。

1. 项目入口 Program.cs

Program.cs 是整个应用程序的入口点。在其中,包含了 CreateHostBuilder 和 Main 方法。CreateHostBuilder 主要负责加载 Startup 配置类,并返回 HostBuilder 对象。这个过程中,它会配置 Web 应用程序的运行环境,如选择要使用的启动类和配置默认的 Web 服务器。在 Main 方法中,通过调用 CreateHostBuilder 方法得到的 HostBuilder 来构建一个 Host 对象,作为 Web API 运行的容器,最后通过 Run 方法启动应用程序。

2. 项目启动配置 Startup

Startup 类有两个关键方法,ConfigureServices 和 Configure。ConfigureServices 用于向容器添加服务,比如在配置 Swagger 时,需要通过这个方法添加相应的服务注册。Configure 方法则用于配置 HTTP 请求管道,例如使用 Swagger 时,需要通过这个方法配置 UseSwagger 和 UseSwaggerUI 等服务。

3. 发布服务配置 launchSettings

launchSettings 是启动服务设置,对应了不同的启动方式,如 IIS Express 和自定义的项目启动方式。其中包含了请求地址、启动 API 等配置信息,为应用程序的发布和调试提供了多种选择。

4. 属性配置文件 appsettings

appsettings 是项目属性配置文件,用于存储各种属性配置。例如,可以在其中添加连接字符串、日志级别配置等。它以 JSON 格式存储配置信息,方便在程序中进行读取和修改。通过在不同的环境下使用不同的 appsettings 文件,可以实现灵活的配置管理,满足不同环境下的应用需求。

二、Swagger 的配置与使用


(一)安装与注册服务

Swagger 在.NET Core WebApi 中的安装非常简便。可以通过 NuGet 包管理器进行安装,在工具菜单中选择 “NuGet 包管理器”→“管理解决方案的 NuGet 包”,然后在搜索框中输入 “Swashbuckle.AspNetCore” 进行安装。安装完成后,在 Startup 类的 ConfigureServices 方法中添加相应服务。具体步骤如下:

public void ConfigureServices(IServiceCollection services)

{

    services.AddControllers();

    services.AddSwaggerGen(c =>

    {

        c.SwaggerDoc("v1", new OpenApiInfo { Title = "API Demo", Version = "v1" });

        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";

        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

        c.IncludeXmlComments(xmlPath, true);

    });

}

首先添加控制器服务,然后注册 Swagger 服务。通过设置 SwaggerDoc 方法指定 API 的版本和标题信息。接着获取当前程序集的 XML 文件名,并结合程序的基础目录得到 XML 文件的完整路径。最后使用 IncludeXmlComments 方法将 XML 文件中的注释包含进来,这样在 Swagger UI 中可以显示接口的详细注释信息。

(二)配置服务

在 Startup 类的 Configure 方法中配置 Swagger 服务,能够更好地管理和测试接口。具体代码如下:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }

    app.UseSwagger();

    app.UseSwaggerUI(c =>

    {

        c.SwaggerEndpoint("/swagger/v1/swagger.json", "API Demo v1");

    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>

    {

        endpoints.MapControllers();

    });

}

如果处于开发环境,则使用开发者异常页面以便更好地调试。接着依次添加 Swagger 中间件和 Swagger UI 中间件。在配置 SwaggerUI 时,通过 SwaggerEndpoint 方法指定 Swagger JSON 端点的路径和名称,使得 Swagger UI 能够正确地显示 API 文档。然后配置路由、授权和端点映射,确保应用程序能够正确地处理请求。这样配置后,开发人员可以通过访问特定的 URL(如 xxx/Swagger/index.html)进入 Swagger 接口列表,方便地查看和测试接口。

三、依赖注入与分层开发


(一)依赖注入概念与实现

依赖注入是一种实现松散耦合的技术,将依赖关系注入到容器中。在ASP.NETCORE 中的容器为 IServiceProvider 接口表示,任何人可以实现自己的容器,只支持构造函数注入这个容器微软默认注入了一些框架本身的服务对象。生命周期有三种:Transient(多例,每一次请求服务都创建一个新实例)、Scoped(每个作用域生成周期内创建一个实例)、Singleton(单例服务,从当前服务容器中获取这个类型的实例永远是同一个实例)。

在项目中实现服务注入的步骤如下:

修改 Services 和 Controller 以使用依赖注入。以获取用户信息为例,修改后的服务实现类如下:

public class UserInfoServices : IUserInfoServices

{

    private readonly IUserInfoRepository _rep;

    public UserInfoServices(IUserInfoRepository rep)

    {

        _rep = rep;

    }

    public UserInfoDto GetUserInfo()

    {

        var entity = _rep.GetUserInfo();

        UserInfoDto dto = new UserInfoDto { UserName = entity.UserName, Age = entity.Age, Address = entity.Address };

        return dto;

    }

}

修改后的控制器如下:

public class UserInfoController : Controller

{

    private readonly IUserInfoServices _services;

    public UserInfoController(IUserInfoServices services)

    {

        _services = services;

    }

    [HttpGet]

    public UserInfoDto GetUserInfo()

    {

        return _services.GetUserInfo();

    }

}

在 Startup 类的 ConfigureServices 方法中注入服务:

public void ConfigureServices(IServiceCollection services)

{

    services.AddControllers();

    services.AddScoped<IUserInfoRepository, UserInfoRepository>();

    services.AddScoped<IUserInfoServices, UserInfoServices>();

}

(二)不考虑分层开发的示例

在不考虑分层开发的情况下,获取用户信息的实现如下:

创建实体类:

public class UserInfo

{

    public string UserName { get; set; }

    public int Age { get; set; }

    public string Address { get; set; }

}

创建传输对象:

public class UserInfoDto

{

    public string UserName { get; set; }

    public int Age { get; set; }

    public string Address { get; set; }

}

创建仓储接口与实现类:

public interface IUserInfoRepository

{

    UserInfo GetUserInfo();

}

public class UserInfoRepository : IUserInfoRepository

{

    public UserInfo GetUserInfo()

    {

        return new UserInfo() { UserName = "王二麻子", Age = 20, Address = "那美克星" };

    }

}

创建服务接口与实现类:

public interface IUserInfoServices

{

    UserInfoDto GetUserInfo();

}

public class UserInfoServices : IUserInfoServices

{

    private readonly IUserInfoRepository _rep;

    public UserInfoServices(IUserInfoRepository rep)

    {

        _rep = rep;

    }

    public UserInfoDto GetUserInfo()

    {

        var entity = _rep.GetUserInfo();

        UserInfoDto dto = new UserInfoDto { UserName = entity.UserName, Age = entity.Age, Address = entity.Address };

        return dto;

    }

}

创建 Controller:

public class UserInfoController : Controller

{

    private readonly IUserInfoServices _services;

    public UserInfoController(IUserInfoServices services)

    {

        _services = services;

    }

    [HttpGet]

    public UserInfoDto GetUserInfo()

    {

        return _services.GetUserInfo();

    }

}

在不考虑分层开发的情况下,服务注入的方法与分层开发时类似,即在 Startup 类的 ConfigureServices 方法中注入服务:

public void ConfigureServices(IServiceCollection services)

{

    services.AddControllers();

    services.AddScoped<IUserInfoRepository, UserInfoRepository>();

    services.AddScoped<IUserInfoServices, UserInfoServices>();

}

四、项目结构与环境搭建


(一)项目结构示例

包含数据访问层、应用层和通用帮助类的项目结构

在这种项目结构中,通常会有明确的分层。数据访问层负责与数据库进行交互,应用层处理业务逻辑,通用帮助类提供一些通用的工具和方法。

例如,在数据访问层,可以创建一个DbContext类来连接数据库,并定义实体类和数据库表的对应关系。以连接 MySQL 数据库为例,首先通过 NuGet 安装MySql.Data.EntityFrameworkCore包,然后创建一个继承自DbContext的类,如:

  using Microsoft.EntityFrameworkCore;

  namespace WebApi.Models

  {

      public class CoreDbContext : DbContext

      {

          public virtual DbSet<Person> Person { get; set; }

          public CoreDbContext(DbContextOptions<CoreDbContext> options) : base(options)

          {

          }

      }

  }

在应用层,可以创建服务类来处理业务逻辑。服务类可以依赖数据访问层的DbContext来获取数据。例如,创建一个用户服务类:

  public interface IUserService

  {

      List<User> GetUsers();

  }

  public class UserService : IUserService

  {

      private readonly CoreDbContext _context;

      public UserService(CoreDbContext context)

      {

          _context = context;

      }

      public List<User> GetUsers()

      {

          return _context.Users.ToList();

      }

  }

通用帮助类可以提供一些常用的工具方法,如数据库连接字符串的读取、数据转换等。例如,可以创建一个SqlHelper类来执行数据库查询:

  using System.Data;

  using System.Data.SqlClient;

  namespace WebApi.Helpers

  {

      public class SqlHelper

      {

          public static string ConnectionString { get; set; }

          public static DataTable ExecuteTable(string query, params SqlParameter[] parameters)

          {

              using (SqlConnection conn = new SqlConnection(ConnectionString))

              {

                  conn.Open();

                  SqlCommand cmd = new SqlCommand(query, conn);

                  cmd.Parameters.AddRange(parameters);

                  SqlDataAdapter sda = new SqlDataAdapter(cmd);

                  DataSet ds = new DataSet();

                  sda.Fill(ds);

                  return ds.Tables[0];

              }

          }

      }

  }

不做过多分层的项目结构

在不做过多分层的项目结构中,可能会将业务逻辑和数据访问混合在一个控制器中。例如,直接在控制器中连接数据库并获取数据:

  using Microsoft.AspNetCore.Mvc;

  using System.Collections.Generic;

  using System.Linq;

  using WebApi.Models;

  namespace WebApi.Controllers

  {

      [Route("api/[controller]")]

      [ApiController]

      public class ValuesController : ControllerBase

      {

          // GET api/values

          [HttpGet]

          public List<Person> Get()

          {

              using (CoreDbContext _coreDbContext = new CoreDbContext())

              {

                  return _coreDbContext.Set<Person>().ToList();

              }

          }

      }

  }

(二)环境搭建

安装 MySQL

在 Windows 系统中,可以通过以下步骤安装 MySQL:

下载 MySQL 安装包,可以从官方网站下载。

运行安装程序,按照向导进行安装。在安装过程中,可以设置用户名、密码和数据库端口等参数。

安装完成后,可以使用可视化工具如 Navicat 连接到 MySQL 数据库进行测试。

在.NET Core WebApi 项目中连接 MySQL 数据库,可以通过 NuGet 安装MySql.Data.EntityFrameworkCore包,然后在Startup类的ConfigureServices方法中添加数据库上下文服务:

  public void ConfigureServices(IServiceCollection services)

  {

      services.AddDbContext<CoreDbContext>(options =>

          options.UseMySQL(Configuration.GetConnectionString("DefaultConnection")));

      services.AddControllers();

  }

安装 Redis

在 Windows 平台可以使用小皮(phpstudy)或在 Linux 平台使用宝塔来搭建 Redis 服务。

在项目中使用 Redis,可以通过 NuGet 安装StackExchange.Redis包。然后在appsettings.json配置文件中添加 Redis 连接配置:

  "Redis": {

      "Default": {

          "Connection": "127.0.0.1:6379,password=d0d493hGlqePd2Tn",

          "InstanceName": "local",

          "DefaultDB": "8"

      }

  }

在Program.cs类中添加服务依赖:

  #region 配置Redis

  var section = builder.Configuration.GetSection("Redis:Default");

  string _connectionString = section.GetSection("Connection").Value;

  string _instanceName = section.GetSection("InstanceName").Value;

  int _defaultDB = int.Parse(section.GetSection("DefaultDB").Value?? "0");

  builder.Services.AddSingleton(new RedisHelper(_connectionString, _instanceName, _defaultDB));

  #endregion

安装.NET 环境

下载并安装.NET Core SDK,可以从官方网站下载。

安装 Visual Studio 2019 或更高版本,选择安装ASP.NETCore 开发相关的工作负载。

创建新项目时,选择ASP.NETCore Web 应用程序,选择.NET Core 版本和项目模板,如 API 模板。

五、字段扩展处理程序的实现


(一)动态修改数据库

在使用 API 开发过程中,传统的 Entity Framework(EF)更新方式存在效率低下和开发繁琐的问题。例如,当一个表中有大量字段时,若只需要更新部分字段,使用传统方法可能需要编写大量的_context.Entry(model).Property(m => m.Field).IsModified = false;或_context.Entry(model).Property(m => m.Field).IsModified = true;语句,这极大地降低了开发效率。

为了解决这个问题,可以采用动态传参[dynamic]的方式。然而,dynamic不支持跨域,因此首先需要解决跨域问题。在Startup.cs文件中,可以添加如下代码来实现跨域设置:

public void ConfigureServices(IServiceCollection services)

{

    //跨域设置

    services.AddCors(options =>

    {

        options.AddPolicy("allow_all", builder =>

        {

            builder.AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin();

            //允许所有域名访问

            //builder.WithOrigins("http://localhost:8088").AllowAnyHeader();

            //允许指定域名访问

        });

    });

}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpContextAccessor httpContextAccessor)

{

    app.UseCors("allow_all");

}

跨域问题解决后,就可以添加动态修改数据库的方法。以下是具体的实现步骤:

序列化动态 Json 为字符串:

  string json = modelNew.ToString();

反序列化为数据表中的实体对象:

  T model = JsonConvert.DeserializeObject<T>(json);

把状态全部变为不可更改:

  _context.Entry(model).State = EntityState.Unchanged;

反序列化为动态对象中的属性:

  var jsonModel = JsonConvert.DeserializeObject<dynamic>(json);

定义一个List来添加属性:

  List<string> listName = new List<string>();

动态添加要修改的字段:

  foreach (PropertyInfo info in model.GetType().GetProperties())

  {

      //如果 EF 表中有实体对象,则排除,否则更新会报错,保留枚举

      if ((info.PropertyType.IsClass && info.PropertyType == typeof(String)) || info.PropertyType.IsClass == false)

      {

          //解决大小写问题

          foreach (var property in jsonModel)

          {

              if (info.Name.ToLower().Trim() == property.Name.ToLower().Trim())

              {

                  listName.Add(info.Name);

              }

          }

      }

  }

寻找主键:

  PropertyInfo pkProp = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0).FirstOrDefault();

遍历修改,并排除主键:

  foreach (string Name in listName)

  {

      if (Name.ToLower()!= pkProp.Name.ToLower())

      {

          _context.Entry(model).Property(Name).IsModified = true;

      }

  }

(二)数据塑形

数据塑形是指对源数据进行一系列操作后,得到各种各样不同形状的数据。在前后端分离开发的今天,前端通过调用后端提供的 API 接口实现页面数据的展示。但有时候,前端并不需要完整的业务实体数据,而只需要其中的某个字段或某些字段。这时,数据塑形就显得尤为重要,它可以提高系统的性能和响应时间,根据不同的 API 消费者反馈不同的数据字段。

实现数据塑形的方法如下:创建一个静态类,编写数据塑形的扩展方法。例如:

public static class DataShapeService

{

    public static IEnumerable<ExpandoObject> GetPairs<T>(this IEnumerable<T> t, string field)

    {

        var pairs = new List<ExpandoObject>(t.Count());

        if (string.IsNullOrWhiteSpace(field))

        {

            var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);

            foreach (var item in t)

            {

                var pair = new ExpandoObject();

                pairs.Add(pair);

            }

            return pairs;

        }

        //... 后续代码根据具体需求进行完善

    }

}

六、总结与展望


(一)关键步骤总结

使用.NET Core WebApi 开发字段扩展处理程序主要涉及以下关键步骤:

1. 动态修改数据库

通过动态传参的方式,避免了传统 Entity Framework 更新方式中繁琐的代码编写。首先解决跨域问题,确保不同域之间的访问顺畅。然后,序列化动态 Json 为字符串,反序列化为实体对象和动态对象,将实体对象状态设置为不可更改,再根据动态对象中的属性动态添加要修改的字段,最后遍历修改并排除主键。这一过程提高了数据库更新的灵活性和效率。

例如,在实际项目中,当需要更新一个包含大量字段的实体时,只需要传递需要更新的字段信息,而不需要对整个实体进行更新,大大减少了网络传输的数据量和服务器的处理时间。

2. 数据塑形

数据塑形是为了满足不同 API 消费者的需求,根据前端的具体要求返回特定形状的数据。通过创建静态类并编写扩展方法,实现对源数据的筛选和转换。在方法中,根据传入的字段参数,决定返回的数据形状。如果字段参数为空,则返回所有公共属性的空扩展对象;如果有特定字段参数,则根据该字段进行数据筛选和转换。

例如,在一个电商系统中,前端可能只需要商品的名称和价格信息,而不需要完整的商品实体数据。通过数据塑形,可以快速地返回满足前端需求的数据,提高系统的响应速度和性能。

(二)优势分析

灵活性:使用动态传参和数据塑形技术,使得开发人员可以根据不同的业务需求灵活地调整数据的形状和内容。无论是数据库的更新还是数据的返回,都可以根据实际情况进行定制化处理,满足不同场景下的需求。

性能提升:通过减少不必要的数据传输和处理,提高了系统的性能和响应时间。在大数据量的情况下,这种优势更加明显,可以有效降低服务器的负载和网络带宽的占用。

易于维护:代码结构清晰,各个功能模块相对独立,易于维护和扩展。开发人员可以根据业务的发展和变化,轻松地添加新的功能或修改现有功能,而不会对整个系统造成较大的影响。

(三)未来发展潜力

与前端框架的深度融合:随着前端技术的不断发展,如 Vue、React 等框架的广泛应用,.NET Core WebApi 可以进一步与这些前端框架深度融合,提供更加高效、便捷的数据交互方式。例如,通过实现实时数据推送、异步数据加载等功能,提高用户体验。

智能化数据处理:结合人工智能和机器学习技术,实现智能化的数据塑形和数据库更新。例如,根据用户的历史行为和偏好,自动调整数据的返回形状,提供个性化的服务。同时,利用机器学习算法预测数据库的更新需求,提前进行数据准备,提高系统的响应速度。

微服务架构的支持:在微服务架构下,.NET Core WebApi 可以作为独立的服务模块,为不同的微服务提供数据支持。通过良好的接口设计和数据契约,实现微服务之间的高效通信和协作。同时,利用容器化技术,如 Docker 和 Kubernetes,实现快速部署和扩展,提高系统的可靠性和可维护性。

总之,使用.NET Core WebApi 开发字段扩展处理程序具有很大的优势和发展潜力。在未来的开发中,我们可以不断探索和创新,将其应用于更多的实际项目中,为企业和用户提供更加高效、便捷的服务。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容