Ідентичність ASP.NET із базою даних EF First MVC5


88

Чи можна використовувати новий Asp.net Identity з Database First та EDMX? Або лише з кодом спочатку?

Ось що я зробив:

1) Я створив новий проект MVC5 і запропонував новій ідентифікації створити нові таблиці користувачів і ролей у моїй базі даних.

2) Потім я відкрив свій файл бази даних First EDMX і перетягнув нову таблицю Identity Users, оскільки у мене є інші таблиці, які стосуються цього.

3) Після збереження EDMX генератор бази даних First POCO автоматично створить клас користувача. Однак UserManager та RoleManager очікує клас користувача, який успадковується з нового простору імен Identity (Microsoft.AspNet.Identity.IUser), тому використання класу користувача POCO не буде працювати.

Я думаю, можливим рішенням є редагування моїх класів генерації POCO, щоб мій клас користувача успадковувався від IUser?

Або ASP.NET Identity сумісний лише з Code First Design?

++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++

Оновлення: Відповідно до пропозиції Андерса Абеля нижче, це те, що я зробив. Це працює, але мені цікаво, чи є більш елегантне рішення.

1) Я розширив свій клас User User, створивши частковий клас в тому ж просторі імен, що й мої автоматично згенеровані сутності.

namespace MVC5.DBFirst.Entity
{
    public partial class AspNetUser : IdentityUser
    {
    }
}

2) Я змінив мій DataContext для успадкування від IdentityDBContext замість DBContext. Зверніть увагу, що кожного разу, коли ви оновлюєте свій EDMX та регенеруєте класи DBContext та Entity, вам доведеться встановлювати для цього значення.

 public partial class MVC5Test_DBEntities : IdentityDbContext<AspNetUser>  //DbContext

3) У межах вашого автоматично згенерованого класу сутності User, ви повинні додати ключове слово override до наступних 4 полів або прокоментувати ці поля, оскільки вони успадковані від IdentityUser (Крок 1). Зверніть увагу, що кожного разу, коли ви оновлюєте свій EDMX та регенеруєте класи DBContext та Entity, вам доведеться встановлювати для цього значення.

    override public string Id { get; set; }
    override public string UserName { get; set; }
    override public string PasswordHash { get; set; }
    override public string SecurityStamp { get; set; }

1
у вас є зразок коду вашої реалізації? як і при спробі повторити вищезазначене, при спробі ввійти в систему або зареєструвати користувача з’являється повідомлення про помилку. "Тип сутності AspNetUser не є частиною моделі для поточного контексту", де AspNetUser - це моя сутність користувача
Тим

Ви додали таблицю AspNetUser до свого EDMX? Крім того, переконайтеся, що ваш AccountController використовує MVC5Test_DBEntities (або будь-який інший контекст вашої БД), а не ApplicationContext.
Patrick Tran

8
ASP.NET Identity - це парі ____. Жахлива підтримка для баз даних спочатку, відсутність документації, погані посилальні обмеження (відсутні на CASCADE DELETE на сервері SQL) і використовує рядки для ідентифікаторів (проблема продуктивності та фрагментація індексу). І це їх 297-та спроба створення ідентичності ...
DeepSpace101,

1
@ DeepSpace101 Identity підтримує DB-first так само, як Code-first. Шаблони налаштовані робити код першим, тому, якщо ви починаєте з шаблону, вам доведеться змінити деякі речі. Каскадне видалення працює чудово, ви можете легко змінити рядки на ints. Дивіться мою відповідь нижче.
Взуття

1
@Shoe Я повинен сказати, що я думаю, що ти можеш помилитися. Я ще не знайшов робочого, вичерпного прикладу / підручника про те, як це реалізувати в db-first (документації немає). API намагається посилатися на таблицю з'єднань "IdentityUserRoles" через властивість IdentityUser.Roles, яка порушує зв'язок на EF db-first, оскільки таблиці з'єднань не виставляються як сутності (погані посилальні обмеження). Я не згоден з рядками для ідентифікаторів, оскільки їх можна налаштувати, вказавши параметри типу в успадкованих класах. Підводячи підсумок, мені здається, що вони взагалі не мали на увазі БД.
Lopsided

Відповіді:


16

Має бути можливим використання системи ідентифікації з POCO і Database First, але вам доведеться зробити кілька налаштувань:

  1. Оновіть файл .tt для генерації POCO, щоб створити класи сутності partial. Це дозволить вам подати додаткову реалізацію в окремий файл.
  2. Зробіть часткову реалізацію Userкласу в іншому файлі

 

partial User : IUser
{
}

Це змусить Userклас реалізувати правильний інтерфейс, не торкаючись фактично створених файлів (редагування створених файлів - це завжди погана ідея).


Дякую. Мені доведеться спробувати і повідомити, якщо це вдасться.
Патрік Тран

Я спробував те, що ви згадали. Дивіться мій пост для оновленої інформації ... але рішення було не дуже елегантним: /
Патрік Тран

У мене була така сама проблема. Здається, що документація щодо DB-first дуже скупа. Це гарна пропозиція, але, я думаю, ви маєте рацію, це не зовсім працює
Філ

4
Чи є ще якесь рішення, яке не є хаком?
user20358

Я використовую Onion Architecture, і всі POCO знаходяться в Core. Там не рекомендується використовувати IUser. Отже, будь-яке інше рішення?
Усман Халід

13

Мої кроки дуже схожі, але я хотів поділитися ними.

1) Створіть новий проект MVC5

2) Створіть нову модель.edmx. Навіть якщо це нова база даних і не має таблиць.

3) Відредагуйте web.config і замініть цей згенерований рядок підключень:

<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-SSFInventory-20140521115734.mdf;Initial Catalog=aspnet-SSFInventory-20140521115734;Integrated Security=True" providerName="System.Data.SqlClient" />

за допомогою цього рядка підключення:

<add name="DefaultConnection" connectionString="Data Source=.\SQLExpress;database=SSFInventory;integrated security=true;" providerName="System.Data.SqlClient" />

Потім побудуйте та запустіть програму. Зареєструйте користувача, і тоді таблиці будуть створені.


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

10

EDIT: ASP.NET Identity with EF Database First для шаблону проекту MVC5 CodePlex.


Я хотів використовувати існуючу базу даних і створити взаємозв'язки з ApplicationUser. Ось як я це зробив, використовуючи SQL Server, але та сама ідея, ймовірно, працювала б з будь-якою БД.

  1. Створіть проект MVC
  2. Відкрийте БД, зазначену в розділі DefaultConnection у Web.config. Він буде викликаний (aspnet- [позначка часу] або щось подібне.)
  3. Сценарій таблиць бази даних.
  4. Вставте скриптові таблиці в існуючу базу даних у SQL Server Management Studio.
  5. Налаштуйте та додайте відносини до ApplicationUser (за потреби).
  6. Створити новий веб-проект> MVC> Перший проект DB> Імпортувати DB за допомогою EF ... Без урахування вставлених класів ідентичності.
  7. У IdentityModels.cs змініть ApplicationDbContext, :base("DefaltConnection")щоб використовувати DbContext вашого проекту.

Редагувати: Діаграма класу ідентичності Asp.Net введіть тут опис зображення


6
Проблема не в DBContext, а в тому, що UserManager та RoleManager очікують клас, який успадковує від Microsoft.AspNet.Identity.EntityFramework.IdentityUser
Патрік Тран

відкритий клас IdentityDbContext <TUser>: DbContext де TUser: Microsoft.AspNet.Identity.EntityFramework.IdentityUser. При першому використанні бази даних сформовані класи сутності не успадковуються від будь-яких базових класів.
Патрік Тран

Потім просто виключіть їх з бази даних, коли ви створюєте класи за допомогою сутності.
смерд

Якщо виключити таблиці ідентичності з EDMX, то ви втратите властивості навігації в інших класах, які мають зовнішні ключі до вашого UserID
Patrick Tran

34
Я не хочу спочатку переходити до коду ... Просто в деяких сценаріях та компаніях адміністратор db створює базові таблиці, а не кодер.
Патрік Тран

8

IdentityUserтут нічого не варто, оскільки це перший об’єкт коду, який використовується UserStoreдля аутентифікації. Визначивши власний Userоб’єкт, я реалізував частковий клас, який реалізує IUserцей UserManagerклас. Я хотів, щоб мої Ids були intзамість рядка, тому я просто повертаю UserID's toString (). Точно так же я хотів nв Usernameбути записати малими.

public partial class User : IUser
{

    public string Id
    {
        get { return this.UserID.ToString(); }
    }

    public string UserName
    {
        get
        {
            return this.Username;
        }
        set
        {
            this.Username = value;
        }
    }
}

Вам ні в якому разі не потрібно IUser. Це лише інтерфейс, який використовується UserManager. Отже, якщо ви хочете визначити інший "IUser", вам доведеться переписати цей клас, щоб використовувати власну реалізацію.

public class UserManager<TUser> : IDisposable where TUser: IUser

Тепер ви написати свій власний , UserStoreякий обробляє всі сховища користувачів, претензій, ролей і т.д. Реалізувати інтерфейси все , що код-перше UserStoreробить і зміни where TUser : IdentityUserдо where TUser : Userде «Користувач» Ваш об'єкт об'єкт

public class MyUserStore<TUser> : IUserLoginStore<TUser>, IUserClaimStore<TUser>, IUserRoleStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserStore<TUser>, IDisposable where TUser : User
{
    private readonly MyAppEntities _context;
    public MyUserStore(MyAppEntities dbContext)
    { 
        _context = dbContext; 
    }

    //Interface definitions
}

Ось кілька прикладів деяких реалізацій інтерфейсу

async Task IUserStore<TUser>.CreateAsync(TUser user)
{
    user.CreatedDate = DateTime.Now;
    _context.Users.Add(user);
    await _context.SaveChangesAsync();
}

async Task IUserStore<TUser>.DeleteAsync(TUser user)
{
    _context.Users.Remove(user);
    await _context.SaveChangesAsync();
}

Використовуючи шаблон MVC 5, я змінив так, AccountControllerщоб виглядати так.

public AccountController()
        : this(new UserManager<User>(new MyUserStore<User>(new MyAppEntities())))
{
}

Тепер вхід повинен працювати з власними таблицями.


1
Нещодавно я мав можливість реалізувати це, і з якихось причин (я вважаю, через оновлення ідентифікації 3.0) я не зміг реалізувати логін, успадкувавши IdentityUser, а потім замінивши властивості; але написання користувацького UserStore та успадкування IUser працювали нормально; Просто даючи оновлення, можливо, хтось вважає це корисним.
Наз Екін

Чи можете ви дати посилання для всієї реалізації інтерфейсу або для повного проекту?
DespeiL

чи є конкретна причина, чому ви використовували тут частковий клас?
user8964654

Якщо ви використовуєте edmx для створення своїх моделей, ви повинні використовувати частковий клас. Якщо ви спочатку робите код, тоді можете пропустити це
Взуття


3

Хороше питання.

Я скоріше особа з бази даних. Перша парадигма коду видається мені невимушеною, а "міграції" здаються занадто схильними до помилок.

Я хотів налаштувати схему ідентифікації aspnet, і не турбувати мене перенесеннями. Я добре обізнаний з проектами баз даних Visual Studio (sqlpackage, data-dude) і з тим, як він досить добре справляється з оновленням схем.

Моє спрощене рішення:

1) Створіть проект бази даних, який відображає схему ідентифікації aspnet 2) використовуйте результати цього проекту (.dacpac) як ресурс проекту 3) розгортайте .dacpac за потреби

Що стосується MVC5, модифікація ApplicationDbContextкласу, здається, призводить до цього ...

1) Впровадити IDatabaseInitializer

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDatabaseInitializer<ApplicationDbContext> { ... }

2) У конструкторі подайте сигнал про те, що цей клас реалізує ініціалізацію бази даних:

Database.SetInitializer<ApplicationDbContext>(this);

3) Реалізуйте InitializeDatabase:

Тут я вирішив використовувати DacFX і розгорнути свій .dacpac

    void IDatabaseInitializer<ApplicationDbContext>.InitializeDatabase(ApplicationDbContext context)
    {
        using (var ms = new MemoryStream(Resources.Binaries.MainSchema))
        {
            using (var package = DacPackage.Load(ms, DacSchemaModelStorageType.Memory))
            {
                DacServices services = new DacServices(Database.Connection.ConnectionString);
                var options = new DacDeployOptions
                {
                    VerifyDeployment = true,
                    BackupDatabaseBeforeChanges = true,
                    BlockOnPossibleDataLoss = false,
                    CreateNewDatabase = false,
                    DropIndexesNotInSource = true,
                    IgnoreComments = true,

                };
                services.Deploy(package, Database.Connection.Database, true, options);
            }
        }
    }

2

Я провів кілька годин, працюючи над цим, і нарешті знайшов рішення, яким поділився у своєму блозі тут . По суті, вам потрібно зробити все сказане у відповіді smink , але з однією додатковою річчю: забезпечення Identity Framework має певний рядок підключення SQL-клієнта поверх рядка підключення Entity Framework, що використовується для ваших сутностей додатків.

Таким чином, ваша програма використовуватиме рядок підключення для Identity Framework, а інший - для сутностей вашого додатка. Кожен рядок підключення має інший тип. Прочитайте мій допис у блозі для повного підручника.


Я двічі пробував усе, що ви згадали у своєму блозі, у мене не вийшло.
Бадхон Джейн

@Badhon Мені дуже шкода, що інструкції в моєму дописі в блозі для вас не спрацювали. Я мав незліченну кількість людей, які висловлювали свою подяку, оскільки вони досягли успіху після моєї статті. Завжди пам’ятайте, що якщо Microsoft щось оновить, це може вплинути на результат. Я написав статтю для ASP.NET MVC 5 з Identity Framework 2.0. Будь-що поза цим може мати проблеми, але поки що я отримав нещодавні коментарі, що свідчать про успіх. Я хотів би почути більше про вашу проблему.
Даніель Ігл,

Я спробую ще раз прослідкувати, проблема, з якою я зіткнувся, полягає в тому, що я не міг користуватися власною базою даних, вона використовувала автоматично побудовану базу даних. У будь-якому випадку, я не хотів сказати щось неправильно з вашою статтею. Дякуємо, що поділилися своїми знаннями.
Бадхон Джейн

1
@Badhon Не турбуйся, друже, я ніколи не відчував, що ти говориш, що зі статтею щось не так. Ми всі маємо унікальні ситуації з різними випадками, тому іноді те, що підходить для інших, може не обов'язково працювати для нас. Сподіваюсь, вам вдалося вирішити вашу проблему.
Даніел Ігл,

2

Я виявив, що @ JoshYates1980 має найпростішу відповідь.

Після ряду спроб і помилок я зробив те, що запропонував Джош, і замінив на connectionStringмій генерований рядок з'єднання з БД. Що мене спочатку бентежило, це наступний пост:

Як додати автентифікацію ідентифікації ASP.NET MVC5 до існуючої бази даних

Там, де у прийнятій відповіді @Win вказано змінити назву ApplicationDbContext()з'єднання. Це трохи розмито, якщо ви використовуєте Entity та перший підхід до бази даних / моделі, де рядок підключення до бази даних генерується та додається до Web.configфайлу.

Назва ApplicationDbContext()з'єднання: відображається у типовому підключенні у Web.configфайлі. Тому метод Джоша працює найкраще, але для того, щоб зробити його ApplicationDbContext()більш читабельним, я б запропонував змінити ім'я на ім'я вашої бази даних як @Win, розміщене спочатку, обов'язково змінивши connectionStringзначення "DefaultConnection" у Web.configі прокоментувавши та / або видаливши Сутність сформована база даних включає.

Приклади коду:


1

У нас є проект DLL Entity Model, де ми зберігаємо наш клас моделей. Ми також зберігаємо проект бази даних зі всіма сценаріями баз даних. Мій підхід був таким

1) Створіть свій власний проект, який спочатку використовує EDMX, використовуючи базу даних

2) Сценарій таблиць у вашій базі даних, я використовував VS2013, підключений до localDB (Data Connections), і скопіював сценарій до проекту бази даних, додав будь-які власні стовпці, наприклад BirthDate [DATE] не null

3) Розгортання бази даних

4) Оновіть проект Model (EDMX) Додати до проекту Model

5) Додайте будь-які власні стовпці до класу програми

public class ApplicationUser : IdentityUser
{
    public DateTime BirthDate { get; set; }
}

У проект MVC AccountController додано таке:

Постачальник посвідчень хоче, щоб для його роботи працював рядок підключення SQL, щоб зберігати лише 1 рядок підключення для бази даних, витягнути рядок постачальника з рядка підключення EF

public AccountController()
{
   var connection = ConfigurationManager.ConnectionStrings["Entities"];
   var entityConnectionString = new EntityConnectionStringBuilder(connection.ConnectionString);
        UserManager =
            new UserManager<ApplicationUser>(
                new UserStore<ApplicationUser>(
                    new ApplicationDbContext(entityConnectionString.ProviderConnectionString)));
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.