asp.net core webapi+efcore

打印 上一主题 下一主题

主题 1479|帖子 1479|积分 4447

简洁的restfull风格

目前c#提供了多种风格的web编程,由于微软有自己的前端,所以集成了很多内容,不过基于现在编程前后端分离的模式,webapi是符合的。
webapi

目前网络上有很多先容,不反复说这个了。在建立控制器时,使用最多就是httpget和httppost,别的意义不大。
实操例子

准备库

 在visual studio建立webapi项目,默认即可。我使用的数据库是postgresql,下载对应的组件。
我用到的所有库:Microsoft.EntityFrameworkCore,Microsoft.EntityFrameworkCore.Tools,Microsoft.EntityFrameworkCore.Proxies,Npgsql.EntityFrameworkCore.PostgreSQL。别的都会项目创建主动有。
Microsoft.EntityFrameworkCore.Tools:先容代码优先会用到。
Microsoft.EntityFrameworkCore.Proxies:先容efcore优化用到。
Npgsql.EntityFrameworkCore.PostgreSQL:PostgreSQL数据库连接驱动
项目编程

(1)创建models文件夹,创建user类,Product类
  1.   public class User
  2.   {
  3.       public int Id { get; set; }
  4.       public string Name { get; set; }
  5.       public string Email { get; set; }
  6.   }
复制代码
  1. public class Product
  2. {
  3.      public int Id { get; set; }
  4.      public string Name { get; set; }
  5.      public decimal Price { get; set; }
  6. }
复制代码
(2)创建PlatDbContext文件夹,创建AppDbContext类,数据库访问。
  1. public class AppDbContext : DbContext
  2. {
  3.      public DbSet<Product> Products { get; set; }
  4.      public DbSet<User> Users { get; set; }
  5.      public AppDbContext(DbContextOptions<AppDbContext> options)
  6.          : base(options) { }
  7.      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  8.      {
  9.          //配置连接数据库字符串
  10.          string connectionString = "Host=localhost;Username=postgres;Password=123456;Database=plat";
  11.          //指定使用SqlServer数据库进行连接
  12.          optionsBuilder.UseNpgsql(connectionString);
  13.        //  optionsBuilder.UseLazyLoadingProxies();
  14.      }
  15.      protected override void OnModelCreating(ModelBuilder modelBuilder)
  16.      {
  17.          modelBuilder.Entity<Product>()
  18.            //  .ToTable("Products")
  19.              .HasKey(p => p.Id);
  20.          modelBuilder.Entity<Product>()
  21.              .Property(p => p.Name)
  22.              .HasMaxLength(100);
  23.      }
  24. }
复制代码
(3) 创建Repository文件夹,创建创存模式的接口和实现类
  1. public interface IRepository<T> where T : class
  2. {
  3.      Task<IEnumerable<T>> GetAllAsync();
  4.      Task<T> GetByIdAsync(int id);
  5.      Task AddAsync(T entity);
  6.      Task UpdateAsync(T entity);
  7.      Task DeleteAsync(int id);
  8.      Task<bool> SaveChangesAsync();
  9.      //只读
  10.      Task<IEnumerable<T>> GetAllAsNoTrackingAsync();
  11.      Task<int> ExecuteSqlAsync(string sql);
  12.      Task<IDbContextTransaction> GetTransactionAsync();
  13. }
复制代码
  1.    public class Repository<T> : IRepository<T> where T : class
  2.    {
  3.        private readonly AppDbContext _context;
  4.        private readonly DbSet<T> _dbSet;
  5.        public Repository(AppDbContext context)
  6.        {
  7.            _context = context;
  8.            _dbSet = _context.Set<T>();
  9.        }
  10.        public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
  11.        public async Task<T> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
  12.        public async Task AddAsync(T entity)
  13.        {   await _dbSet.AddAsync(entity);
  14.            await SaveChangesAsync();
  15.        }
  16.        public async Task UpdateAsync(T entity)
  17.        {
  18.            _dbSet.Update(entity);
  19.            await SaveChangesAsync();
  20.        }
  21.        public async Task DeleteAsync(int id)
  22.        {
  23.            var entity = await _dbSet.FindAsync(id);
  24.            if (entity != null)
  25.            {
  26.                _dbSet.Remove(entity);
  27.                await SaveChangesAsync();
  28.            }
  29.        }
  30.        public async Task<IEnumerable<T>> GetAllAsNoTrackingAsync()
  31.        {
  32.          return  await _dbSet.AsNoTracking<T>().ToListAsync();
  33.        }
  34.      
  35.        public async Task<int> ExecuteSqlAsync(string sql)
  36.        {
  37.            return await _context.Database.ExecuteSqlRawAsync(sql);
  38.        }
  39.        public async Task<IDbContextTransaction> GetTransactionAsync()
  40.        {
  41.          return  await _context.Database.BeginTransactionAsync();
  42.        }
  43.        public async Task<bool> SaveChangesAsync() => await _context.SaveChangesAsync() > 0;
  44.    }
复制代码
(4)配置EF,在main方法中添加如下代码。
  1. builder.Services.AddDbContext<AppDbContext>(options =>
  2. options.UseNpgsql().LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name }));
  3. // builder.Services.AddDbContextPool<AppDbContext>(options => options.UseNpgsql(), poolSize: 80);
  4. //注意设置最大连接数,一旦超过默认配置的连接池最大数量,会回退到按需创建实例的行为
  5. builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
复制代码
(5) 创建控制器UserController 
  1. namespace WebPlat.Controllers
  2. {
  3.     [Route("api/[controller]")]
  4.     [ApiController]
  5.     public class UserController : ControllerBase
  6.     {
  7.         public  UserController(IRepository<User> repository,ILogger<UserController> logger)
  8.         {
  9.             this.repository = repository;
  10.             this.logger = logger;
  11.         }
  12.         IRepository<User> repository;
  13.         ILogger<UserController> logger;
  14.         [HttpGet]
  15.         [Route("GetUser")]
  16.         public IEnumerable<User> GetUser()
  17.         {
  18.             // 返回所有用户的逻辑
  19.             return new List<User>
  20.         {
  21.             new User { Id = 1, Name = "Alice", Email = "alice@example.com" },
  22.             new User { Id = 2, Name = "Bob", Email = "bob@example.com" }  
  23.             // ... 其他用户数据  
  24.         };
  25.         }
  26.         // GET api/Test/5  
  27.         [HttpGet]
  28.         [Route("GetMyUser")]
  29.         public User GetMyUser(int id)
  30.         {
  31.             // 根据ID返回单个用户的逻辑  
  32.             return new User { Id = 1, Name = "Alice", Email = "alice@example.com" };
  33.         }
  34.         [HttpPost("AddUser")]
  35.         public bool AddUser([FromBody] User user)
  36.         {
  37.             repository.AddAsync(user);
  38.             return true;
  39.         }
  40.       
  41.     }
  42. }
复制代码
(6) 服务层(简朴的步调不需要)
如果你有很复杂的业务处理,那么还需要一个服务层,需要创建文件夹Services,在里面创建接口和实现类,也有把接口和类放到不同的文件夹(名称空间)。此时存储对象就需要放在业务层。控制器中使用服务接口。
  1. public interface IUserService
  2. {
  3.      Task<User> GetUserByIdAsync(int id);
  4.      Task<IEnumerable<User>> GetAllUsersAsync();
  5.      Task<User> CreateUserAsync(User user);
  6.      Task UpdateUserAsync(int id, User user);
  7.      Task DeleteUserAsync(int id);
  8. }
复制代码
  1. public class UserService : IUserService
  2. {
  3.      private readonly IRepository<User>  repository; // 假设有一个 ApplicationDbContext 作为数据上下文
  4.    
  5.      public UserService(IRepository<User> repository)
  6.      {
  7.          this.repository = repository;
  8.      }
  9.      
  10.      public async Task<User> GetUserByIdAsync(int id)
  11.      {
  12.          return await repository.GetByIdAsync(id);
  13.      }
  14.      public async Task<IEnumerable<User>> GetAllUsersAsync()
  15.      {
  16.          return await repository.GetAllAsync();
  17.      }
  18.      public async Task<User> CreateUserAsync(User user)
  19.      {
  20.          await repository.AddAsync(user);
  21.          return user;
  22.      }
  23.      public async Task UpdateUserAsync(int id, User user)
  24.      {
  25.          await repository.UpdateAsync(id, user);
  26.      }
  27.      public async Task DeleteUserAsync(int id)
  28.      {
  29.          await repository.DeleteAsync(id);
  30.      }
  31. }
复制代码
然后注入服务。
  1. builder.Services.AddScoped(typeof(IUserService), typeof(UserService));
复制代码
不好意思我偷了一下懒,不提倡。写了一个反射方法注入服务。
  1. public static void AddService(IServiceCollection services)
  2. {
  3.      // 获取当前程序集
  4.      Assembly assembly = Assembly.GetExecutingAssembly();
  5.      // 指定要搜索的命名空间
  6.      string targetNamespace = "WebPlat.Services";
  7.      // 获取所有类型
  8.      Type[] types = assembly.GetTypes();
  9.      // 过滤出特定命名空间下的所有类型
  10.      var filteredTypes = types.Where(t => t.Namespace == targetNamespace).Where(t=>t.IsClass);
  11.      foreach (var type in filteredTypes)
  12.      {
  13.          Type[] interfaces= type.GetInterfaces();
  14.          var interfacesTypes=  interfaces.Where(t => t.Namespace == targetNamespace);//可以不要,只是更加精确
  15.          if (interfaces.Length > 0)
  16.          {
  17.              foreach(Type interfacetype in interfacesTypes)
  18.              {
  19.                  services.AddScoped(interfacetype, type);
  20.              }
  21.            
  22.          }
  23.      }
  24. }
复制代码
在main方法注入服务就变成了调用该方法。
所以控制器中就变了一点,为了演示没有删除之前的。
 public  UserController(IRepository<User> repository,IUserService service, ILogger<UserController> logger)
 {
     this.repository = repository;
     this.logger = logger;
     this.userService = service;
 }
 IRepository<User> repository;
 ILogger<UserController> logger;
 IUserService userService;
添加了一个测试方法:
  1.   [HttpPost("CreateUser")]
  2.   public bool CreateUser([FromBody] UserVo user)
  3.   {
  4.       // repository.AddAsync(user);
  5.      string json= JsonUtil.ToJson(user);
  6.       User user1= JsonUtil.ToObject<User>(json);
  7.       user1= CopyUtil<UserVo, User>.Trans(user);
  8.       userService.CreateUserAsync(user1);
  9.       return true;
  10.   }
复制代码
有一点不一样?由于如果有复杂业务,一般来说前端的数据和最后数据库的数据可能不一样,就会专门有和前端交互的数据结构,就是dao层。有时间还有中心的实体层等。
既然有了2个不一样的类结构,是不是要赋值?c#浅拷贝,深拷贝了解一下。
这里一般主要的几种:c#自己的序列化(二进制,xml,json),手动写代码赋值,第三方等。
通用的就是序列化,目前主要是json,别的两种就别给自己找事了。
json序列化目前保举使用第三方Newtonsoft.Json,不要用微软的,除非你自己闲的慌喜好尝试。
因此又建立一个工具文件夹Utils,创建json序列化类。你也限定一下泛型参数。
  1.   public class JsonUtil
  2.   {
  3.       public  static  string ToJson<T>(T obj)
  4.       {
  5.           string json = JsonConvert.SerializeObject(obj);
  6.           return json;
  7.       }
  8.       public static T ToObject<T>(string json)
  9.       {
  10.           T obj = JsonConvert.DeserializeObject<T>(json);
  11.           return obj;
  12.       }
  13.   }
复制代码
如许就可以通过json复制对象,json复制对象类似反射,服从相对有点低,但是一般的步调应该是可以的,可以接受。
顺便提供一个优化的复制对象方法,表达式树。创建CopyUtil类。
  1. public static class CopyUtil<TIn, TOut>
  2. {
  3.      private static readonly Func<TIn, TOut> cache = GetFunc();
  4.      private static Func<TIn, TOut> GetFunc()
  5.      {
  6.          ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
  7.          List<MemberBinding> memberBindingList = new List<MemberBinding>();
  8.          foreach (var item in typeof(TOut).GetProperties())
  9.          {
  10.              if (!item.CanWrite)
  11.                  continue;
  12.              if(typeof(TIn).GetProperty(item.Name)==null)
  13.                  {
  14.                  continue;
  15.              }
  16.              MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
  17.              MemberBinding memberBinding = Expression.Bind(item, property);
  18.              memberBindingList.Add(memberBinding);
  19.          }
  20.          MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
  21.          Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
  22.          return lambda.Compile();
  23.      }
  24.      public static TOut Trans(TIn tIn)
  25.      {
  26.          return cache(tIn);
  27.      }
  28. }
复制代码
你可能不懂?不懂没有关系,反正就是复制属性。以前我研究过emit,写了一个通用方法,嗯服从很高,不过看起来有点反人类,这个表达式树比较通人性一点。两者原理一样,都是在内存中已经编译生成了。
EF的代码优先

下载完库,运行项目可以正常执行以后,准备生成数据库的表,这里我是先容一下操作,不详细讲解ef的使用方法,ef的使用,在model上可以设置各种属性,我不详细先容,太大。
(1)数据库   
   安装好postgresql数据库,客户端连接,建立好数据库:plat。
(2)生成数据库表
在visual studio的工具菜单中打开“步调包管理器控制台”,然后执行命令:Add-Migration InitialCreate 执行成功后,会在项目中生成一个文件夹和类,不消管。
然后再执行命令: Update-database 
以上成功则会在数据库生成表,如图:


补充

项目中创建一个全局筛选器,可以做很多有效的工作。例如项目的错误处理。
(1)项目中创建文件夹Filter,建立HttpExceptionFilterAttribute类
  1. public class HttpExceptionFilterAttribute : System.Web.Http.Filters.ExceptionFilterAttribute
  2. {
  3.      public override void OnException(HttpActionExecutedContext actionExecutedContext)
  4.      {
  5.          if (actionExecutedContext.Exception is Exception ex)
  6.          {
  7.              actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(
  8.                  HttpStatusCode.InternalServerError, ex.Message);
  9.          }
  10.      }
  11. }
复制代码
EF优化

EFcore速率一直是比较有争议,不过目前最新版本感觉已经基本可以了,这里把最主要的优化简朴先容。
(1)耽误加载
启用耽误配置,在AppDbContext的配置方法中添加配置语句。该功能的详细意义需要自己详细了解。
  1. optionsBuilder.UseLazyLoadingProxies();
复制代码
(2)上下文池化
需要再配置上下文时使用池化。用该语句替换一般的配置。
  1. builder.Services.AddDbContextPool<AppDbContext>(options => options.UseNpgsql(), poolSize: 80);
复制代码
(3)查询只读,启用AsNoTracking.
就是只查询不跟踪,在项目中就是仓存模式的GetAllAsNoTrackingAsync方法。
目前主要的优化就是上面几种,当然对于步调来说,你可以根据业务优化,在查询中启用缓存的方式等。还有一些别的库,例如:Z.EntityFramework.Plus.EFCore。该第三方库可以研究。
这里说一下批量添加,批量添加目前很多数据库支持批量sql语句,例如:
insert  into(ID,name)value(1,'ton') value(2,'j') ;如许的语句,可以充实使。
还有很多数据库提供了bulkcopy,支持ado.net方式,所以有了efcore后,原始底子的ado.net照旧英华,需要了解。
最后的总结

  这里只是webapi,其实不喜好代码优先大概数据库优先这种模式。个人觉得数据库照旧数据库,代码照旧代码,传统的数据库计划创建,步调开发就是步调,这里model会少很多属性,数据库表会很准确。目前如许的方式出现,很多小企业都是步调员自己弄数据库,开发步调,看起来会少很多工作,其实不好。照旧分离好点,还有助于检测。
  别的你是winfrom和WPF等桌面系统,也建议可以使用后台服务webapi。目前已经不需要用IIS部署了。如许显示端是web或桌面都无所谓。至于性能题目,可以在某些功能下使用TCP大概grpc。
桌面系统访问建议使用httpclient库。 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

来自云龙湖轮廓分明的月亮

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表