仓库模式下如何将Pokemon API数据传入Controller并添加至EF DataContext
问题描述
我创建了一个Web API控制器,用于从公开宝可梦API获取列表并添加到SQL数据库,采用了仓库模式,但遇到了问题:不知道如何将仓库中反序列化的对象传递到控制器并通过DataContext存入数据库。仓库里的HTTP请求、反序列化逻辑没问题,但在Swagger测试时出现类型不兼容的提示,尝试过Stack Overflow上的方案但没成功,求建议。
项目相关代码
PokemonDtoesController.cs
namespace MyPokeAPI.Controllers { [Route("api/[controller]")] [ApiController] public class PokemonDtoesController : ControllerBase { private readonly DataContext _context; private readonly IPokemonRepository repository; public PokemonDtoesController(DataContext context, IPokemonRepository _repository) { this.repository = _repository; _context = context; } // GET: api/PokemonDtoes [HttpGet] public async Task<ActionResult<IEnumerable<PokemonDto>>> GetPokemonDto() { var items = (await repository.GetAllPokemonsAsync()); _context.Add(items); return _context; } // GET: api/PokemonDtoes/5 [HttpGet("{id}")] public async Task<ActionResult<PokemonDto>> GetPokemonDto(int id) { var pokemonDto = await _context._pokemons.FindAsync(id); if (pokemonDto == null) { return NotFound(); } return pokemonDto; } private bool PokemonDtoExists(int id) { return _context._pokemons.Any(e => e.id == id); } } }
PokemonRepository.cs
namespace MyPokeAPI.Repositories { public class PokemonRepository : IPokemonRepository { private HttpClient _client; private PokemonDto currPoke = null; private string json = string.Empty; int limit = 30; private readonly List<PokemonDto> pokemons = new List<PokemonDto>(); public async Task<IEnumerable<PokemonDto>> GetAllPokemonsAsync() { string url = $"https://pokeapi.co/api/v2/pokemon/?offset={limit}&limit={limit}"; using (_client = new HttpClient()) { _client.BaseAddress = new Uri(url); _client.DefaultRequestHeaders.Accept.Clear(); _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = await _client.GetAsync(url); if (response.IsSuccessStatusCode) { json = await response.Content.ReadAsStringAsync(); BaseResults myDeserializedClass = JsonConvert.DeserializeObject<BaseResults>(json); if (myDeserializedClass != null) { foreach (var result in myDeserializedClass.results) { using (HttpClient insideclient = new HttpClient()) { insideclient.BaseAddress = new Uri(result.url); response = await insideclient.GetAsync(result.url); if (response.IsSuccessStatusCode) { json = await response.Content.ReadAsStringAsync(); //AbilitiesRoot currPokeAbilities = JsonConvert.DeserializeObject<AbilitiesRoot>(json); currPoke = JsonConvert.DeserializeObject<PokemonDto>(json); if (currPoke != null) { pokemons.Add(currPoke); } } } } } } } return pokemons; } public PokemonDto GetSinglePokemonAsync(string name) { return pokemons.Where(item => item.name == name).SingleOrDefault(); } } }
DataContext.cs
namespace MyPokeAPI.Helper { public class DataContext : DbContext { protected readonly IConfiguration Configuration; public DataContext(IConfiguration configuration) { this.Configuration = configuration; } protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnectionString")); } public DbSet<PokemonDto> _pokemons { get; set; } } }
解决方案
1. 修复控制器的Get方法(核心问题)
你的GetPokemonDto方法存在三个关键错误:
- 用
_context.Add(items)添加集合:EF Core的Add方法用于单个实体,集合需要用AddRange - 返回
_context:方法返回类型是ActionResult<IEnumerable<PokemonDto>>,但你返回了DataContext,类型完全不匹配,这就是Swagger提示类型不兼容的原因 - 未调用
SaveChangesAsync:添加实体后必须保存才能写入数据库
修复后的代码:
[HttpGet] public async Task<ActionResult<IEnumerable<PokemonDto>>> GetPokemonDto() { var items = await repository.GetAllPokemonsAsync(); // 用AddRange批量添加集合 _context._pokemons.AddRange(items); // 异步保存到数据库 await _context.SaveChangesAsync(); // 返回获取到的宝可梦列表,符合方法返回类型 return Ok(items); }
2. 修复仓库的重复数据问题
仓库中pokemons是类级别的私有字段,每次调用GetAllPokemonsAsync都会往同一个列表添加数据,重复调用会导致大量重复实体。应该把列表移到方法内部:
public async Task<IEnumerable<PokemonDto>> GetAllPokemonsAsync() { int limit = 30; string url = $"https://pokeapi.co/api/v2/pokemon/?offset={limit}&limit={limit}"; // 每次调用创建新列表,避免重复数据 var pokemons = new List<PokemonDto>(); using (var _client = new HttpClient()) { _client.DefaultRequestHeaders.Accept.Clear(); _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = await _client.GetAsync(url); if (response.IsSuccessStatusCode) { var json = await response.Content.ReadAsStringAsync(); BaseResults myDeserializedClass = JsonConvert.DeserializeObject<BaseResults>(json); if (myDeserializedClass != null) { foreach (var result in myDeserializedClass.results) { using (var insideclient = new HttpClient()) { response = await insideclient.GetAsync(result.url); if (response.IsSuccessStatusCode) { json = await response.Content.ReadAsStringAsync(); var currPoke = JsonConvert.DeserializeObject<PokemonDto>(json); if (currPoke != null) { pokemons.Add(currPoke); } } } } } } } return pokemons; }
3. 优化HttpClient使用(可选但推荐)
每次创建新HttpClient会导致socket资源耗尽,建议用IHttpClientFactory注入复用:
- 修改仓库构造函数:
private readonly HttpClient _client; public PokemonRepository(HttpClient client) { _client = client; }
- 在Program.cs中注册:
builder.Services.AddHttpClient<IPokemonRepository, PokemonRepository>();
4. 验证实体配置
确保PokemonDto类的主键(比如id字段)被EF Core正确识别,可通过数据注解或Fluent API配置:
public class PokemonDto { [Key] public int id { get; set; } // 其他字段... }
内容的提问来源于stack exchange,提问作者Pablo Costa
相关产品推荐
相关产品推荐

