使用EF与Web API开发电商用户层时登录遇空引用错误
电商网站用户层登录空引用错误排查
问题背景
基于Entity Framework和Web API构建电商网站用户层,调用登录API时触发空引用错误,无法定位具体空值来源,怀疑与GetList方法或AutoMapper映射相关。
核心代码展示
1. EF基础仓储实现
namespace Core.DataAccess.EntityFramework { public class EfBaseRepository<TEntity, TContext> : IBaseRepository<TEntity> where TEntity : class, IEntity, new() where TContext : DbContext, new() { public async Task<TEntity> AddAsync(TEntity entity) { using (TContext context = new TContext()) { await context.Set<TEntity>().AddAsync(entity); await context.SaveChangesAsync(); return entity; } } public async Task<bool> DeleteAsync(int id) { using (TContext context = new TContext()) { var deleteEntity = await context.Set<TEntity>().FindAsync(id); context.Set<TEntity>().Remove(deleteEntity); var data = await context.SaveChangesAsync(); if (data > 0) return true; return false; } } public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> filter) { using (TContext context = new TContext()) { return await context.Set<TEntity>().SingleOrDefaultAsync(filter); } } public async Task<IEnumerable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> filter = null) { using (TContext context = new TContext()) { return filter == null ? await context.Set<TEntity>().ToListAsync() : await context.Set<TEntity>().Where(filter).ToListAsync(); } } public async Task<TEntity> UpdateAsync(TEntity entity) { using (TContext context = new TContext()) { context.Set<TEntity>().Update(entity); await context.SaveChangesAsync(); return entity; } } } }
2. UserService业务逻辑
namespace Business.Concrete { public class UserService : IUserService { private readonly IUserDal _userDal; AppSettings _appSettings; IMapper _mapper; public UserService(IUserDal userDal, IOptions<AppSettings> appSettings, IMapper mapper) { _userDal = userDal; _appSettings = appSettings.Value; _mapper = mapper; } public async Task<ApiDataResponse<UserDto>> AddAsync(UserAddDto userAddDto) { var user = _mapper.Map<User>(userAddDto); user.CreatedDate = DateTime.UtcNow; user.CreatedUserId = 1; var userAdd = await _userDal.AddAsync(user); var userDto = _mapper.Map<UserDto>(userAdd); return new SuccessApiDataResponse<UserDto>(userDto, Messages.Added); } public async Task<ApiDataResponse<bool>> DeleteAsync(int id) { return new SuccessApiDataResponse<bool>(await _userDal.DeleteAsync(id), Messages.Deleted); } public async Task<ApiDataResponse<UserDto>> GetByIdAsync(int id) { var user = await _userDal.GetAsync(x => x.Id == id); if (user != null) { var userDto = _mapper.Map<UserDto>(user); return new SuccessApiDataResponse<UserDto>(userDto, Messages.Listed); } return new ErrorApiDataResponse<UserDto>(null, Messages.NotListed); } public async Task<ApiDataResponse<IEnumerable<UserDetailDto>>> GetListAsync(Expression<Func<User, bool>> filter = null) { if (filter == null) { var response = await _userDal.GetListAsync(); var userDetailDtos = _mapper.Map<IEnumerable<UserDetailDto>>(response); return new SuccessApiDataResponse<IEnumerable<UserDetailDto>>(userDetailDtos, Messages.Listed); } else { var response = await _userDal.GetListAsync(filter); var userDetailDtos = _mapper.Map<IEnumerable<UserDetailDto>>(response); return new SuccessApiDataResponse<IEnumerable<UserDetailDto>>(userDetailDtos, Messages.Listed); } } public async Task<ApiDataResponse<UserDto>> GetAsync(Expression<Func<User, bool>> filter = null) { var user = await _userDal.GetAsync(filter); if (user!= null) { var userDto= _mapper.Map<UserDto>(user); return new SuccessApiDataResponse<UserDto>(userDto, Messages.Listed); } return new ErrorApiDataResponse<UserDto>(null, Messages.NotListed); } public async Task<ApiDataResponse<UserUpdateDto>> UpdateAsync(UserUpdateDto userUpdateDto) { var getUser = await _userDal.GetAsync(x => x.Id == userUpdateDto.Id); var user = _mapper.Map<User>(userUpdateDto); user.CreatedDate = getUser.CreatedDate; user.CreatedUserId = getUser.CreatedUserId; user.UpdatedDate = DateTime.Now; user.UpdatedUserId = 1; user.Token = userUpdateDto.Token; user.TokenExpireDate = userUpdateDto.TokenExpireDate; var resultUpdate = await _userDal.UpdateAsync(user); var userUpdateMap = _mapper.Map<UserUpdateDto>(resultUpdate); return new SuccessApiDataResponse<UserUpdateDto>(userUpdateMap, Messages.Updated); } } }
3. AuthService登录逻辑
namespace Business.Concrete { public class AuthService : IAuthService { private IUserService _userService; private ITokenService _tokenService; private IMapper _mapper; public AuthService(IUserService userService, ITokenService tokenService, IMapper mapper) { _userService = userService; _tokenService = tokenService; _mapper = mapper; } public async Task<ApiDataResponse<UserDto>> LoginAsync(LoginDto loginDto) { var user = await _userService.GetAsync(x => x.UserName == loginDto.UserName && x.Password == loginDto.Password); if (user == null) { return new ErrorApiDataResponse<UserDto>(null, Messages.UserNotFound); } else { if (user.Data.TokenExpireDate == null || String.IsNullOrEmpty(user.Data.Token)) { var accessToken = _tokenService.CreateToken(user.Data.Id, user.Data.UserName); var userUpdateDto = _mapper.Map<UserUpdateDto>(user.Data); userUpdateDto.Token = accessToken.Token; userUpdateDto.TokenExpireDate = accessToken.Expiration; userUpdateDto.UpdatedUserId = user.Data.Id; var resultUserUpdateDto = await _userService.UpdateAsync(userUpdateDto); var userDto = _mapper.Map<UserDto>(resultUserUpdateDto.Data); return new SuccessApiDataResponse<UserDto>(userDto, Messages.LoginSuccessful); } if (user.Data.TokenExpireDate < DateTime.Now) { var accessToken = _tokenService.CreateToken(user.Data.Id, user.Data.UserName); var userUpdateDto = _mapper.Map<UserUpdateDto>(user.Data); userUpdateDto.Token = accessToken.Token; userUpdateDto.TokenExpireDate = accessToken.Expiration; userUpdateDto.UpdatedUserId = user.Data.Id; var resultUserUpdateDto = await _userService.UpdateAsync(userUpdateDto); var userDto = _mapper.Map<UserDto>(resultUserUpdateDto.Data); return new SuccessApiDataResponse<UserDto>(userDto, Messages.LoginSuccessful); } } return new SuccessApiDataResponse<UserDto>(user.Data, Messages.LoginSuccessful); } } }
空引用错误定位与修复
1. AuthService中错误的空判断逻辑
UserService.GetAsync永远不会返回null,它会返回SuccessApiDataResponse或ErrorApiDataResponse对象。当用户不存在时,user.Data为null,直接访问user.Data.TokenExpireDate会触发空引用。
修复代码:
var user = await _userService.GetAsync(x => x.UserName == loginDto.UserName && x.Password == loginDto.Password); // 替换原有的user == null判断 if (!user.Success || user.Data == null) { return new ErrorApiDataResponse<UserDto>(null, Messages.UserNotFound); }
2. UserService.UpdateAsync中未校验用户存在性
var getUser = await _userDal.GetAsync(x => x.Id == userUpdateDto.Id);可能返回null,后续访问getUser.CreatedDate会触发空引用。
修复代码:
var getUser = await _userDal.GetAsync(x => x.Id == userUpdateDto.Id); if(getUser == null) { return new ErrorApiDataResponse<UserUpdateDto>(null, Messages.UserNotFound); } // 后续映射逻辑
3. EfBaseRepository.DeleteAsync中未校验待删除对象
FindAsync(id)返回null时,Remove(deleteEntity)会触发空引用。
修复代码:
public async Task<bool> DeleteAsync(int id) { using (TContext context = new TContext()) { var deleteEntity = await context.Set<TEntity>().FindAsync(id); if(deleteEntity == null) return false; context.Set<TEntity>().Remove(deleteEntity); var data = await context.SaveChangesAsync(); return data > 0; } }
4. 验证AutoMapper映射配置
检查User与UserDto、UserUpdateDto的映射配置,确保所有必要属性都已正确映射,避免映射后对象属性为null。例如:
CreateMap<User, UserDto>().ReverseMap(); CreateMap<User, UserUpdateDto>().ReverseMap();
内容的提问来源于stack exchange,提问作者Serkan Gün
相关产品推荐
相关产品推荐

