Я сталкиваюсь с одним из тех прекрасных «это сообщение об ошибке слишком общее, чтобы быть полезным», которое периодически появляется при работе с Entity.Может ли кто-нибудь рисковать догадками относительно обстоятельств, которые могут быть причиной этого?Или даже лучше - какой хороший способ определить, что конкретно вызывает ошибку?
Ситуация:
Я настраиваю локальную базу данных на основе идентификаторов создание учетной записи и вход в систему с помощью Web Api 2.0.Мы используем EntityFramework 6.2.0 и AspNet.Identity.Core v2.2.2.Исходя из потребностей приложения, я настроил нас на использование числовых (а не UUID) ключей для пользовательского объекта пользователя, который хранится в той же базе данных / DbContext, что и остальная часть приложения.модель данных.Существуют определенные пользовательские классы с более подробной информацией, чем учетная запись входа, которые имеют отношения FK к пользовательскому объекту пользователя, но данные такого типа еще не существуют.
Ошибка возникает при попытке выполнить довольно простое создание пользователя,В настоящее время аутентификация на основе ролей или чего-либо еще не настроена;Я просто использую PostMan для работы с конечной точкой API локально работающей копии приложения.Запрос PostMan отправляется на правильный контроллер API, и правильно построенный (насколько я могу судить) пользовательский объект ускоряется, но когда мы пытаемся использовать (слегка настроенный) UserManager, чтобы добавить нового пользователя вDB я получаю сообщение об ошибке:
«Операция недопустима из-за текущего состояния объекта.»
К сожалению, я не могу сосредоточиться на чем-либо более точномэто может быть причиной этой ошибки, и я пока не смог найти что-либо от MS, документирующее обстоятельства, при которых она может быть выброшена.
PostMan-ответ / трассировка стека (немного предварительно подтверждено):
{
"message": "An error has occurred.",
"exceptionMessage": "Operation is not valid due to the current state of the object.",
"exceptionType": "System.InvalidOperationException",
"stackTrace": " at APP.Core.Data.Models.APPUserStore.CreateAsync(APPUser user) in C:\\...\\GitHub\\APP\\APP\\APP.Core\\Data\\Models\\APPUserStore.cs:line 23
at Microsoft.AspNet.Identity.UserManager`2.<CreateAsync>d__73.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Identity.UserManager`2.<CreateAsync>d__79.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Identity.AsyncHelper.RunSync[TResult](Func`1 func)
at Microsoft.AspNet.Identity.UserManagerExtensions.Create[TUser,TKey](UserManager`2 manager, TUser user, String password)
at APP.Web.Controllers.api.AuthController.<CreateUser>d__5.MoveNext()
in C:\\...\\GitHub\\APP\\APP\\APP.Web\\Controllers\\api\\AuthController.cs:line 77
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Threading.Tasks.TaskHelpersExtensions.<CastToObject>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Dispatcher.HttpControllerDispatcher<SendAsync>d__15.MoveNext()"
}
Соответствующий код:
in AuthController :
[HttpPost, Route("create")]
public async Task<IHttpActionResult> CreateUser(CreateAccountBindingModel model)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
var user = CoreFactory.Create(model);
var result = await UserMgr.CreateAsync(user, model.Password);
// ERROR OCCURS AFTER EXECUTION OF THE ABOVE LINE ---^
if (!result.Succeeded) return GetErrorResult(result);
var locationHeader = new Uri(Url.Link("GetUserById", new { id = user.Id }));
return Created(locationHeader, VmFactory.Create(user));
}
AuthController наследуется от BaseApiController следующим образом:
public class BaseApiController : ApiController
{
private readonly APPUserManager userMgr = null;
private CoreModelFactory coreFactory;
private ViewModelFactory modelFactory;
protected BaseApiController() { }
protected APPUserManager UserMgr => userMgr ??
Request.GetOwinContext().GetUserManager<APPUserManager>();
protected ViewModelFactory VmFactory => modelFactory ??
(modelFactory = new ViewModelFactory(Request, UserMgr));
protected CoreModelFactory CoreFactory => coreFactory ??
(coreFactory = new CoreModelFactory(
UserMgr,
Request.GetOwinContext().Get<APPContext>()));
protected IHttpActionResult GetErrorResult(IdentityResult result)
{
// This isn't getting called, assume it works
}
}
и соответствующее приложение UserManager :
public class APPUserManager : UserManager<APPUser, int>,
IAPPUserManager
{
public APPUserManager(IUserStore<APPUser, int> store)
: base(store)
{
Log = new APPLog();
}
private IAPPLog Log { get; }
public static APPUserManager Create(
IdentityFactoryOptions<APPUserManager> options,
IOwinContext context)
{
var appDbContext = context.Get<APPContext>();
var manager = new APPUserManager(new APPUserStore(context.Get<APPContext>()))
{
UserLockoutEnabledByDefault = false,
DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5),
MaxFailedAccessAttemptsBeforeLockout = 5
};
manager.UserValidator = new UserValidator<APPUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<APPUser, int>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
public APPUser FindById(int id)
{
return FindByIdAsync(id).Result;
}
}
Вот мой пользовательский объект (обрезанный);Предполагается, что все обязательные поля устанавливаются перед отправкой в постоянство.Мы используем целочисленный идентификатор, и это поле установлено на 0 для отправки в БД.
APPUserLogin , APPUserRole и т. Д., Классы пусты, простонаследование от эквивалентов базы идентификаторов, но с целочисленным идентификатором вместо строкового.
public class APPUser : IdentityUser<int, APPUserLogin, APPUserRole, APPUserClaim>,
IAPPDbModel
{
public APPUser()
{
StatusChangeDate = DateTime.Now;
SecurityStamp = "DEFAULT VALUE";
PasswordHash = "DEFAULT VALUE";
TwoFactorEnabled = false;
LockoutEnabled = false;
AccessFailedCount = 0;
}
[Key]
public int APPUserId { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<APPUser, int> mgr)
{
var userIdentity = await mgr.CreateIdentityAsync(this,
DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
public ClaimsIdentity GenerateUserIdentity(UserManager<APPUser, int> mgr)
{
var userId = mgr.CreateIdentity(this, DefaultAuthenticationTypes.ApplicationCookie);
return userId;
}
}
Для целей этого вопроса отправляемый новый пользователь выглядит следующим образом:
User {APP.Core.Data.Models.APPUser} APP.Core.Data.Models.APPUser
AccessFailedCount 0 int
+ Claims Count = 0 System.Collections.Generic.ICollection<APP.Core.Data.Models.APPUserClaim> {System.Collections.Generic.List<APP.Core.Data.Models.APPUserClaim>}
Email "a_valid_email@foo.foo" string
EmailConfirmed false bool
Id 0 int
LockoutEnabled false bool
LockoutEndDateUtc null System.DateTime?
+ Logins Count = 0 System.Collections.Generic.ICollection<APP.Core.Data.Models.APPUserLogin> {System.Collections.Generic.List<APP.Core.Data.Models.APPUserLogin>}
PasswordHash "DEFAULT" string
+ Roles Count = 0 System.Collections.Generic.ICollection<APP.Core.Data.Models.APPUserRole> {System.Collections.Generic.List<APP.Core.Data.Models.APPUserRole>}
SecurityStamp "DEFAULT" string
Status Active APP.Core.Data.Models.Status
+ StatusChangeDate {(A VALID)} System.DateTime
APPUserId 0 int
TwoFactorEnabled false bool
UserName "A NAME" string
Большинство попаданий, которые я видел в сообщении об ошибке, устарели на несколько лет и связаны с проблемами конфигурации базы данных, которые я не считаю более актуальными.База данных существует, и ее структурой можно управлять с помощью миграций.