Оновлення даних користувача - ідентифікація ASP.NET


75

Я додав власні поля до ApplicationUserкласу.
Я також створив форму, за допомогою якої користувач може вводити / редагувати поля.
Однак з якихось причин я не можу оновити поля в базі даних.

[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(EditProfileViewModel model)
{
    if (ModelState.IsValid)
    {
        // Get the current application user
        var user = User.Identity.GetApplicationUser();

        // Update the details
        user.Name = new Name { First = model.FirstName, Last = model.LastName, Nickname = model.NickName };
        user.Birthday = model.Birthdate;

        // This is the part that doesn't work
        var result = await UserManager.UpdateAsync(user);

        // However, it always succeeds inspite of not updating the database
        if (!result.Succeeded)
        {
            AddErrors(result);
        }
    }

    return RedirectToAction("Manage");
}

Моя проблема схожа на власні властивості MVC5 ApplicationUser , але, схоже, використовується старіша версія Identity, оскільки клас IdentityManager, здається, не існує.

Хтось може допомогти мені, як оновити Userінформацію в базі даних?

ОНОВЛЕННЯ: Якщо я включу всі поля у форму реєстру, всі значення зберігаються у відповідному полі в новому записі Usersтаблиці з бази даних.

Я не знаю, як вносити зміни в поля існуючого користувача (рядок у usersтаблиці). UserManager.UpdateAsync(user)не працює.

Також зверніть увагу, що моя проблема більше орієнтована на ідентичність, ніж EntityFramework


Ви перевіряли виконання оновлення бази даних після змін у ApplicationUser? Також "Назва" є посиланням на клас, воно повинно бути серіалізовано або відповідним чином відображено в OnModelCreating.
jd4u

Ні, в цьому немає проблем, структура бази даних така ж, як ApplicationUser. Поля name - це стовпець у базі даних (як Name_First, Name_Last та Name_NickName). Моя проблема полягає в тому, що база даних файлів не оновлюється новими значеннями, коли я телефоную UserManager.UpdateAsync(user). Я просто хочу знати, як я повинен продовжувати оновлення ApplicationUser (таблиця користувачів)
galdin

Фрагменти коду ApplicationUser та OnModelCreating можуть бути корисними для вирішення цієї проблеми. З вашого коментаря, схоже, проблема зіставлення властивості Name та її частин.
jd4u

@ jd4u Оновляючи ApplicationUser (таблицю користувачів), я мав на увазі значення в таблиці, а не структуру таблиці. Зіставлення є належними і працюють із формою реєстру. Інформацію просто не можна оновити.
galdin

Відповіді:


99

Добре ... Я годинами намагався зрозуміти, чому userManager.updateAsyncб не зберегти користувацькі дані, які ми редагуємо ... поки я не дійшов такого висновку:

Плутанина виникає через те, що ми створюємо UserManagerв один рядок так:

var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDbContext()));

... тоді ми використовуємо, manager.UpdateAsync( user );але це оновить користувача в контексті, і тоді нам потрібно буде зберегти зміни в dbcontext ідентифікатора. Отже, питання полягає в тому, як найпростішим чином отримати Identity DBcontext.

Щоб вирішити це, нам не слід створювати UserManagerв одному рядку ... і ось як я це роблю:

var store = new UserStore<ApplicationUser>(new MyDbContext());
var manager = new UserManager(store);

потім після оновлення користувача за допомогою дзвінка

manager.UpdateAsync(user);

тоді ви переходите до контексту

var ctx = store.context;

тоді

ctx.saveChanges();

wahooooooo ... наполегливо :)

Сподіваюся, це допоможе тому, хто витягнув волосся на кілька годин: P


4
ярлик до останніх двох кроків: store.context.saveChanges ();
stackunderflow

3
нарешті .. для тих, хто потребує повного впровадження, це посилання є чудовим weblogs.asp.net/imranbaloch/archive/2013/12/12/…
stackunderflow

2
Я вважаю, що неправильно викликати updateAsync, а потім два рядки зберігають зміни контексту. Хіба це не буде умовою перегони?
mac10688

1
Ви маєте на увазі, що нам слід використовуватиawait
stackunderflow

1
Чи справді потрібно робити ctx.saveChanges (); після UpdateAsync ()? Я не думаю. І не забудьте наблизити "new MyDbContext ()" до використання блоку.
Олександр

58

Якщо залишити будь-яке з полів ApplicationUser АБО IdentityUser нульовим, оновлення повернеться як успішне, але не збереже дані в базі даних.

Приклад рішення:

ApplicationUser model = UserManager.FindById(User.Identity.GetUserId())

Додайте нещодавно оновлені поля:

model.Email = AppUserViewModel.Email;
model.FName = AppUserViewModel.FName;
model.LName = AppUserViewModel.LName;
model.DOB = AppUserViewModel.DOB;
model.Gender = AppUserViewModel.Gender;

Виклик UpdateAsync

IdentityResult result = await UserManager.UpdateAsync(model);

Я перевірив це, і воно працює.


Я закінчив із симулятором. Щойно використано Automapper для відображення моделі. Це виглядає так: Mapper.Map (AppUserViewModel, Model);
Олексій Аза

це означає, що я не можу дозволити одному з полів залишатися нульовим
galdin

Думаю, IdentityResult result = await UserManager.UpdateAsync(model);для моделі це має бути невеликий м
Jammer

12

Контекст OWIN дозволяє отримати контекст db. Здається, поки що у мене все працює нормально, і врешті-решт, я отримав ідею з класу ApplciationUserManager, який робить те саме.

    internal void UpdateEmail(HttpContext context, string userName, string email)
    {
        var manager = context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var user = manager.FindByName(userName);
        user.Email = email;
        user.EmailConfirmed = false;
        manager.Update(user);
        context.GetOwinContext().Get<ApplicationDbContext>().SaveChanges();
    }

3
Я не думаю, що останній рядок більше не потрібно (це питання було задано, коли Identity було новим). Я щойно перевірив контроль над джерелом нещодавнього проекту і помітив, що я лише це зробив, await UserManager.UpdateAsync(user)і зміни збереглися. Не могли б ви підтвердити?
galdin

1
Я можу підтвердити, що для цього прикладу останній рядок, безумовно, був необхідним для збереження змін у базі даних. Для цього використовується остання стабільна версія Identity 2, доступна для програми веб-форм. Можливо, методи Async діють інакше?
Атерс

Я використовую Owin з IOC (з викликом Update()зсередини несинхронної допоміжної функції), і це єдине рішення, яке працювало зі стандартним контекстом Owin. Вітання за збереження мого дня та +1
Пройшло кодування

5

UserManager не працював, і, як писав @Kevin Junghans,

UpdateAsync просто прив'язує оновлення до контексту, вам все одно потрібно зберегти контекст, щоб воно фіксувалось у базі даних

Ось швидке рішення (до нових функцій в ідентичності ASP.net v2), яке я використовував у веб-формах projetc.

class AspNetUser :IdentityUser

Перенесено з SqlServerMembership aspnet_Users. І контекст визначений:

public partial class MyContext : IdentityDbContext<AspNetUser>

Прошу вибачення за відображення та синхронний код - якщо ви покладете це в асинхронний метод, використовуйте awaitдля асинхронних викликів і видаліть Tasks and Wait () s. Аргумент, props, містить назви властивостей для оновлення.

 public static void UpdateAspNetUser(AspNetUser user, string[] props)
 {
     MyContext context = new MyContext();
     UserStore<AspNetUser> store = new UserStore<AspNetUser>(context);
     Task<AspNetUser> cUser = store.FindByIdAsync(user.Id); 
     cUser.Wait();
     AspNetUser oldUser = cUser.Result;

    foreach (var prop in props)
    {
        PropertyInfo pi = typeof(AspNetUser).GetProperty(prop);
        var val = pi.GetValue(user);
        pi.SetValue(oldUser, val);
    }

    Task task = store.UpdateAsync(oldUser);
    task.Wait();

    context.SaveChanges();
 }

3

У мене також виникли проблеми з використанням UpdateAsync під час розробки версії SimpleSecurity, яка використовує ідентифікацію ASP.NET. Наприклад, я додав функцію скидання пароля, необхідну для додавання маркера скидання пароля до інформації користувача. Спочатку я спробував використовувати UpdateAsync, і він отримав ті самі результати, що і ви. У підсумку я обернув сутність користувача шаблоном репозиторію і змусив його працювати. Для прикладу можна подивитися проект SimpleSecurity . Попрацювавши з ASP.NET Identity більше (документація все ще не існує), я думаю, що UpdateAsync просто фіксує оновлення до контексту, вам все одно потрібно зберегти контекст, щоб він фіксувався в базі даних.


Так, `UpdateAsync просто прив'язує оновлення до контексту, [...] збережіть контекст для того, щоб його було
зафіксовано

3

Я спробував функціональність таким же чином, і коли я викликаю UserManager.Updateasync метод, це вдається, але оновлення в базі даних немає. Потративши деякий час, я знайшов інше рішення для оновлення даних у aspnetusersтаблиці, яке є таким:

1) вам потрібно створити UserDbContextклас, що успадковує його, IdentityDbContextнаприклад:

public class UserDbContext:IdentityDbContext<UserInfo>
{
    public UserDbContext():
        base("DefaultConnection")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }
}

2) тоді в Обліковому записі контролера облікового запису оновіть інформацію про користувача, як це

UserDbContext userDbContext = new UserDbContext();
userDbContext.Entry(user).State = System.Data.Entity.EntityState.Modified;
await userDbContext.SaveChangesAsync();

де userваша оновлена ​​сутність.

сподіваюся, це допоможе вам.


2

Відмінно !!!

IdentityResult result = await UserManager.UpdateAsync(user);

Я знаю, що це працює зараз, хоча тоді це не працює: p На той час фреймворк ідентичності був відносно новим
galdin

Якщо це працює, я використовую VS 2013 MVC 5 - EntityFramework, версія = 6.
Макс.

MVC 5 використовував Identity 2.0, так? Це питання досить давнє, як я вже сказав.
galdin

Я не знаю, чи чиню це зло, яке використовую, чи повинен я його змінити?
Макс.

Версія system.Web.Mvc: 5.2.3.0
Макс.

1

На основі вашого запитання, а також зазначеного в коментарі

Хтось може допомогти мені, як оновити інформацію про користувача в базі даних?

Так, код правильний для оновлення будь-якого ApplicationUserдо бази даних.

IdentityResult result = await UserManager.UpdateAsync(user);

  • Перевірте обмеження всіх необхідних значень поля
  • Перевірка UserManager створюється за допомогою ApplicationUser.

UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));


чи не перевіряються обмеження під час перевірки ModelState.IsValid?
galdin

ModelState перевіряє модель перегляду. Ви берете ApplicationUser з Db за допомогою GetApplicationUser ().
jd4u

Більше того, UpdateManager.Update просто виконує одну перевірку для імені користувача в реалізації за замовчуванням перед тим, як відправити виклик оновлення до UserStore. Спробуйте використовувати UserStore безпосередньо для налагодження.
jd4u

2
@anyone, Позначаючи будь-яку відповідь як не корисну, будь ласка, прочитайте все та майте на увазі трохи контексту. Ця відповідь відповідає контексту. Більше того, вибрана тут відповідь не є рішенням, її альтернативний підхід ігнорує основну структуру Identity Framework і безпосередньо працює з базою даних.
jd4u

0

Це працює для мене. Я використовую Identity 2.0, схоже, GetApplicationUser більше немає.

        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (!string.IsNullOrEmpty(form["FirstName"]))
        {
            user.FirstName = form["FirstName"];
        }
        if (!string.IsNullOrEmpty(form["LastName"]))
        {
            user.LastName = form["LastName"];
        }
        IdentityResult result = await UserManager.UpdateAsync(user);

GetApplicationUser()є спеціальним методом розширення
galdin

0

Я використовую нове ядро ​​EF & Identity Core і маю ту саму проблему, крім того, що у мене виникла така помилка:

Екземпляр типу сутності неможливо відстежити, оскільки інший екземпляр цього типу з тим самим ключем вже відстежується.

З новою моделлю DI я додав Контролер конструктора контекст до БД.

Я спробував зрозуміти, у чому конфлікт _conext.ChangeTracker.Entries()і що додавAsNoTracking() до своїх дзвінків без успіху.

Мені потрібно лише змінити стан мого об’єкта (в даному випадку ідентифікаційний)

_context.Entry(user).State = EntityState.Modified;
var result = await _userManager.UpdateAsync(user);

І працював, не створюючи іншого магазину чи об’єкта та зіставлення.

Сподіваюся, комусь іншому будуть корисні мої два центи.


0

Я використовую .Net Core 3.1 або новішу версію. Будь ласка, дотримуйтесь рішення:

  public class UpdateAssignUserRole
    {
        public string username { get; set; }
        public string rolename { get; set; }
        public bool IsEdit { get; set; }

    }

 private async Task UpdateSeedUsers(UserManager<IdentityUser> userManager, UpdateAssignUserRole updateassignUsername)
        {
            IList<Users> Users = await FindByUserName(updateassignUsername.username);

            if (await userManager.FindByNameAsync(updateassignUsername.username) != null)
            {
                var user = new IdentityUser
                {
                    UserName = updateassignUsername.username,
                    Email = Users[0].Email,
 
                };
                var result = await userManager.FindByNameAsync(updateassignUsername.username);
                if (result != null)
                {
                    IdentityResult deletionResult = await userManager.RemoveFromRolesAsync(result, await userManager.GetRolesAsync(result));
                    if (deletionResult != null)
                    {
                        await userManager.AddToRoleAsync(result, updateassignUsername.rolename);
                    }
                }
            }

        }

-1

Додайте наступний код у файл Startup.Auth.cs під статичним конструктором:

        UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

Рядок коду налаштування UserManagerFactory - це те, що ви використовуєте для асоціювання власного DataContext із UserManager. Після цього ви зможете отримати екземпляр UserManager у вашому ApiController, а метод UserManager.UpdateAsync (користувач) буде працювати, оскільки він використовує ваш DataContext для збереження додаткових властивостей, доданих користувачеві вашого користувацького додатка.


Ваш код містить досить багато речей, які не мають відношення до питання. Пропоную вам прибрати.
Zero3
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.