Идентификация ASP.NET с базой данных EF First MVC5

88

Можно ли использовать новую идентификацию Asp.net с Database First и EDMX? Или сначала только с кодом?

Вот что я сделал:

1) Я создал новый проект MVC5, и новая идентификация создала новые таблицы пользователей и ролей в моей базе данных.

2) Затем я открыл свой EDMX-файл Database First и перетащил в новую таблицу Identity Users, поскольку у меня есть другие таблицы, которые к ней относятся.

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

Я предполагаю, что возможное решение - отредактировать мои классы генерации POCO, чтобы мой класс пользователя унаследовал от IUser?

Или ASP.NET Identity совместим только с Code First Design?

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

Обновление: следуя предложению Андерса Абеля ниже, я сделал это именно так. Это работает, но мне интересно, есть ли более элегантное решение.

1) Я расширил свой класс 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.
Патрик Тран
8
ASP.NET Identity - это дымящаяся куча ____. Ужасная поддержка для базы данных в первую очередь, отсутствие документации, плохие ссылочные ограничения (отсутствует ON CASCADE DELETE на SQL-сервере) и используются строки для идентификаторов (проблемы с производительностью и фрагментация индекса). И это их 297-я попытка
создать
1
@ DeepSpace101 Identity поддерживает DB-first так же, как Code-first. Шаблоны настроены так, чтобы сначала выполнять код, поэтому, если вы начнете с шаблона, вам придется кое-что изменить. Каскадное удаление работает нормально, вы можете легко изменить строки на целые числа. Смотрите мой ответ ниже.
Shoe
1
@ Обувь. Должен сказать, что, возможно, ты ошибаешься. Мне еще предстоит найти рабочий, исчерпывающий пример / руководство о том, как реализовать это в 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 08
Я использую Onion Architecture, и все POCO находятся в Core. Там не рекомендуется использовать IUser. Есть ли другое решение?
Усман Халид
13

Мои шаги очень похожи, но я хотел поделиться.

1) Создайте новый проект MVC5

2) Создайте новый Model.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" />

После этого соберите и запустите приложение. Зарегистрируйте пользователя, и тогда таблицы будут созданы.

Джош Ятс 1980
источник
1
это решило проблему, я не мог видеть пользователя приложения в базе данных, но после замены соединения по умолчанию все заработало.
Хассен Ч.
10

РЕДАКТИРОВАТЬ: идентификация ASP.NET с базой данных EF First для шаблона проекта MVC5 CodePlex.


Я хотел использовать существующую базу данных и установить отношения с ApplicationUser. Вот как я сделал это с помощью SQL Server, но та же идея, вероятно, будет работать с любой БД.

  1. Создать проект MVC
  2. Откройте БД, указанную в DefaultConnection в Web.config. Он будет называться (aspnet- [timestamp] или что-то в этом роде.)
  3. Создайте сценарий для таблиц базы данных.
  4. Вставьте таблицы со сценарием в существующую базу данных в SQL Server Management Studio.
  5. Настройте и добавьте отношения к ApplicationUser (при необходимости).
  6. Создайте новый веб-проект> MVC> Первый проект БД> Импортировать БД с 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. При первом использовании базы данных сгенерированные классы сущностей не наследуются ни от каких базовых классов.
Патрик Тран,
Затем просто исключите их из базы данных при создании классов с помощью структуры сущностей.
stink
Если вы исключите таблицы идентификации из EDMX, вы потеряете свойства навигации в других классах, у которых есть внешние ключи для вашего идентификатора пользователя,
Патрик Тран,
34
Я не против сначала перейти к коду ... Просто в некоторых сценариях и компаниях базовые таблицы создает администратор базы данных, а не кодировщик.
Патрик Тран
8

IdentityUserздесь бесполезен, потому что это объект с первым кодом, используемый UserStoreдля аутентификации. После определения моего собственного Userобъекта я реализовал частичный класс, который реализует то, IUserчто используется этим UserManagerклассом. Я хотел, Idчтобы intвместо строки был мой 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 для создания своих моделей, вы должны использовать частичный класс. Если вы сначала пишете код, то можете пропустить это
Shoe
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

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

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

Дэниел Игл
источник
Я дважды перепробовала все, что вы упомянули в своем блоге, у меня не вышло.
Бадхон Джайн
@Badhon Мне очень жаль, что инструкции в моем блоге не сработали для вас. Бесчисленное количество людей выражали свою благодарность за то, что они добились успеха после моей статьи. Всегда помните, что если Microsoft что-то обновит, это может повлиять на результат. Я написал статью для ASP.NET MVC 5 с Identity Framework 2.0. Все, что выходит за рамки этого, может вызвать проблемы, но до сих пор я получил очень недавние комментарии, указывающие на успех. Я хотел бы узнать больше о вашей проблеме.
Daniel Eagle
Я попытаюсь повторить еще раз, проблема, с которой я столкнулся, заключалась в том, что я не мог использовать свою собственную базу данных, она использовала автоматически созданную базу данных. В любом случае, я не хотел сказать что-то не так с вашей статьей. Спасибо, что поделились своими знаниями.
Badhon Jain
1
@Badhon Не волнуйся, друг мой, я никогда не чувствовал, что ты говоришь что-то не так со статьей. У всех нас есть уникальные ситуации с различными крайними случаями, поэтому иногда то, что работает для других, не обязательно работает для нас. Надеюсь, вы смогли решить свою проблему.
Дэниел Игл
2

Я обнаружил, что у @ JoshYates1980 есть самый простой ответ.

После серии проб и ошибок я сделал то, что предложил Джош, и заменил connectionStringсгенерированную строку подключения к БД. Изначально меня смутил следующий пост:

Как добавить проверку подлинности ASP.NET MVC5 в существующую базу данных

В принятом ответе @Win указано, что нужно изменить ApplicationDbContext()имя соединения. Это немного расплывчато, если вы используете первый подход Entity и Database / Model, при котором строка подключения к базе данных создается и добавляется в Web.configфайл.

Имя ApplicationDbContext()подключения сопоставляется с подключением по умолчанию в Web.configфайле. Таким образом, метод Джоша работает лучше всего, но для ApplicationDbContext()большей читабельности я бы предложил изменить имя на имя вашей базы данных, как было первоначально опубликовано @Win, не забудьте изменить connectionStringдля «DefaultConnection» в Web.configи закомментировать и / или удалить Entity сгенерированная база данных включает.

Примеры кода:

TheSchnitz
источник
1

У нас есть проект DLL модели сущностей, в котором мы храним наш класс модели. Мы также ведем проект базы данных со всеми скриптами базы данных. Мой подход был следующим

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

2) Создайте сценарий для таблиц в своей базе данных, я использовал VS2013, подключенный к localDB (подключения к данным), и скопировал сценарий в проект базы данных, добавьте любые настраиваемые столбцы, например, BirthDate [DATE] not null

3) Разверните базу данных

4) Обновите проект модели (EDMX) Добавить в проект модели

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)));
}
Haroon
источник