Identitas ASP.NET dengan EF Database First MVC5

88

Apakah mungkin untuk menggunakan Identitas Asp.net baru dengan Database First dan EDMX? Atau hanya dengan kode dulu?

Inilah yang saya lakukan:

1) Saya membuat Proyek MVC5 baru dan membuat Identitas baru membuat tabel Pengguna dan Peran baru di database saya.

2) Saya kemudian membuka file EDMX Pertama Database saya dan menyeret tabel Pengguna Identitas baru karena saya memiliki tabel lain yang terkait dengannya.

3) Setelah menyimpan EDMX, generator POCO Pertama Database akan otomatis membuat kelas Pengguna. Namun, UserManager dan RoleManager mengharapkan kelas Pengguna mewarisi dari namespace Identity baru (Microsoft.AspNet.Identity.IUser), jadi menggunakan kelas Pengguna POCO tidak akan berfungsi.

Saya kira solusi yang mungkin adalah mengedit Kelas Generasi POCO saya agar kelas Pengguna saya diwarisi dari IUser?

Atau apakah ASP.NET Identity hanya kompatibel dengan Code First Design?

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

Pembaruan: Mengikuti saran Anders Abel di bawah ini, inilah yang saya lakukan. Berhasil, tapi saya bertanya-tanya apakah ada solusi yang lebih elegan.

1) Saya memperluas kelas Pengguna entitas saya dengan membuat kelas parsial dalam namespace yang sama dengan entitas yang saya buat secara otomatis.

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

2) Saya mengubah DataContext saya untuk mewarisi dari IdentityDBContext, bukan DBContext. Perhatikan bahwa setiap kali Anda memperbarui EDMX dan membuat ulang kelas DBContext dan Entitas, Anda harus menyetelnya kembali ke ini.

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

3) Dalam kelas entitas Pengguna yang dibuat secara otomatis, Anda harus menambahkan kata kunci penggantian ke 4 bidang berikut atau beri komentar pada bidang ini karena diwarisi dari IdentityUser (Langkah 1). Perhatikan bahwa setiap kali Anda memperbarui EDMX dan membuat ulang kelas DBContext dan Entitas, Anda harus menyetelnya kembali ke ini.

    override public string Id { get; set; }
    override public string UserName { get; set; }
    override public string PasswordHash { get; set; }
    override public string SecurityStamp { get; set; }
Patrick Tran
sumber
1
apakah Anda memiliki kode contoh penerapan Anda? seperti ketika saya mencoba untuk meniru hal di atas, saya mendapatkan kesalahan ketika saya mencoba untuk masuk atau mendaftarkan pengguna. "Jenis entitas AspNetUser bukan bagian dari model untuk konteks saat ini" di mana AspNetUser adalah entitas Pengguna saya
Tim
Apakah Anda menambahkan tabel AspNetUser ke EDMX Anda? Selain itu, pastikan AccountController Anda menggunakan MVC5Test_DBEntities (atau apa pun nama konteks DB Anda) daripada ApplicationContext.
Patrick Tran
8
ASP.NET Identity adalah tumpukan ____ yang mengepul. Dukungan mengerikan untuk database-first, tidak ada dokumentasi, kendala referensial yang buruk (hilang ON CASCADE DELETE di SQL server) dan menggunakan string untuk ID (masalah kinerja dan fragmentasi indeks). Dan ini adalah upaya ke-297 mereka pada kerangka identitas ...
DeepSpace101
1
@ DeepSpace101 Identity mendukung DB-first sama seperti Code-first. Template diatur untuk melakukan kode terlebih dahulu jadi jika Anda memulai dari template Anda harus mengubah beberapa hal. Penghapusan bertingkat berfungsi dengan baik, Anda dapat mengubah string menjadi int dengan mudah. Lihat jawaban saya di bawah.
Sepatu
1
@Shoe Saya harus mengatakan bahwa saya pikir Anda mungkin salah. Saya belum menemukan contoh / tutorial yang berfungsi dan komprehensif tentang cara menerapkan ini di db-first (tidak ada dokumentasi). API mencoba untuk mereferensikan tabel persimpangan "IdentityUserRoles" melalui properti IdentityUser.Roles, yang memutuskan hubungan pada EF db-first karena tabel persimpangan tidak diekspos sebagai entitas (batasan referensial yang buruk). Saya tidak setuju tentang string untuk ID karena itu dapat disesuaikan dengan menentukan parameter tipe di kelas yang diwariskan. Singkatnya, menurut saya mereka sama sekali tidak memikirkan DB.
Miring

Jawaban:

16

Seharusnya mungkin untuk menggunakan sistem identitas dengan POCO dan Database First, tetapi Anda harus membuat beberapa penyesuaian:

  1. Perbarui file .tt untuk pembuatan POCO untuk membuat kelas entitas partial. Itu akan memungkinkan Anda untuk menyediakan implementasi tambahan dalam file terpisah.
  2. Buat implementasi parsial Userkelas di file lain

 

partial User : IUser
{
}

Itu akan membuat Userkelas mengimplementasikan antarmuka yang benar, tanpa menyentuh file yang dihasilkan sebenarnya (mengedit file yang dihasilkan selalu merupakan ide yang buruk).

Anders Abel
sumber
Terima kasih. Saya harus mencoba ini dan melaporkan jika berhasil.
Patrick Tran
Saya mencoba apa yang Anda sebutkan. Lihat posting saya untuk info terbaru ... tetapi solusinya tidak terlalu elegan: /
Patrick Tran
Saya baru saja mengalami masalah yang sama. Tampaknya dokumentasi tentang DB-first sangat jarang. Ini adalah saran yang bagus tapi saya pikir Anda benar, itu tidak cukup berhasil
Phil
4
Apakah masih ada solusi yang bukan hack?
pengguna20358
Saya menggunakan Arsitektur Onion dan semua POCO ada di Core. Di sana tidak disarankan untuk menggunakan IUser. Jadi ada solusi lain?
Usman Khalid
13

Langkah saya sangat mirip tetapi saya ingin berbagi.

1) Buat proyek MVC5 baru

2) Buat Model.edmx baru. Meskipun itu adalah database baru dan tidak memiliki tabel.

3) Edit web.config dan ganti string koneksi yang dihasilkan ini:

<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" />

dengan connectionstring ini:

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

Setelah itu, buat dan jalankan aplikasi. Daftarkan pengguna dan kemudian tabel akan dibuat.

JoshYates1980
sumber
1
ini memecahkan masalah, saya tidak bisa melihat pengguna aplikasi di database, tetapi setelah mengganti koneksi default, itu berhasil.
Hassen Ch.
10

EDIT: Identitas ASP.NET dengan EF Database First untuk MVC5 Template Proyek CodePlex.


Saya ingin menggunakan database yang sudah ada dan membuat hubungan dengan ApplicationUser. Ini adalah bagaimana saya melakukannya menggunakan SQL Server tetapi ide yang sama mungkin akan bekerja dengan DB apa pun.

  1. Buat Proyek MVC
  2. Buka DB yang terdaftar di bawah DefaultConnection di Web.config. Ini akan disebut (aspnet- [timestamp] atau sesuatu seperti itu.)
  3. Buat skrip tabel database.
  4. Sisipkan tabel skrip ke database yang ada di SQL Server Management Studio.
  5. Sesuaikan dan tambahkan hubungan ke ApplicationUser (jika perlu).
  6. Buat Proyek Web baru> MVC> Proyek Pertama DB> Impor DB dengan EF ... Tidak Termasuk Kelas Identitas yang Anda masukkan.
  7. Di IdentityModels.cs ubah ApplicationDbContext :base("DefaltConnection")untuk menggunakan DbContext proyek Anda.

Edit: Diagram Kelas Identitas Asp.Net masukkan deskripsi gambar di sini

bau
sumber
6
Masalahnya bukan DBContext, tetapi UserManager dan RoleManager mengharapkan kelas yang mewarisi dari Microsoft.AspNet.Identity.EntityFramework.IdentityUser
Patrick Tran
kelas publik IdentityDbContext <TUser>: DbContext di mana TUser: Microsoft.AspNet.Identity.EntityFramework.IdentityUser. Saat menggunakan database terlebih dahulu, kelas entitas yang dihasilkan tidak mewarisi dari kelas dasar apa pun.
Patrick Tran
Kemudian kecualikan saja dari database saat Anda membuat kelas dengan kerangka entitas.
Bau
Jika Anda mengecualikan tabel Identitas dari EDMX, maka Anda akan kehilangan properti Navigasi di kelas lain yang memiliki kunci asing ke UserID Anda
Patrick Tran
35
Saya tidak segan-segan pindah ke kode dulu ... Hanya saja di beberapa skenario dan perusahaan, admin db membuat tabel dasar, bukan pembuat kode.
Patrick Tran
8

IdentityUsertidak ada gunanya di sini karena ini adalah objek kode-pertama yang digunakan oleh UserStoreuntuk otentikasi. Setelah mendefinisikan objek saya sendiri User, saya mengimplementasikan kelas parsial yang mengimplementasikan IUseryang digunakan oleh UserManagerkelas. Saya ingin Ids saya menjadi intbukan string jadi saya hanya mengembalikan UserID's toString (). Demikian pula saya ingin ndi Usernamemenjadi uncapitalized.

public partial class User : IUser
{

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

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

Anda tidak perlu IUser. Ini hanya antarmuka yang digunakan oleh UserManager. Jadi, jika Anda ingin mendefinisikan "IUser" yang berbeda, Anda harus menulis ulang kelas ini untuk menggunakan implementasi Anda sendiri.

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

Anda sekarang menulis milik Anda sendiri UserStoreyang menangani semua penyimpanan pengguna, klaim, peran, dll. Menerapkan antarmuka dari semua yang dilakukan kode pertama UserStoredan ubah where TUser : IdentityUserke where TUser : Usertempat "Pengguna" adalah objek entitas Anda

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
}

Berikut adalah beberapa contoh tentang beberapa implementasi antarmuka

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();
}

Dengan menggunakan template MVC 5, saya mengubah tampilan AccountControllermenjadi seperti ini.

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

Sekarang masuk harus bekerja dengan tabel Anda sendiri.

Sepatu
sumber
1
Saya baru-baru ini memiliki kesempatan untuk menerapkan ini, dan untuk beberapa alasan (saya percaya karena pembaruan identitas 3.0) saya tidak dapat menerapkan login dengan mewarisi IdentityUser dan kemudian menimpa properti; tetapi menulis UserStore kustom dan IUser mewarisi berfungsi dengan baik; Hanya memberikan pembaruan, mungkin seseorang menganggap ini berguna.
Naz Ekin
Bisakah Anda memberikan tautan untuk semua implementasi antarmuka atau untuk proyek penuh?
DespeiL
apakah ada alasan khusus mengapa Anda menggunakan kelas parsial di sini?
user8964654
Jika Anda menggunakan edmx untuk menghasilkan model Anda, Anda harus menggunakan kelas parsial. Jika Anda membuat kode terlebih dahulu maka Anda dapat menghilangkan ini
Sepatu
3

Pertanyaan bagus.

Saya lebih dari orang yang mengutamakan database. Paradigma pertama kode tampaknya membuat saya pusing, dan "migrasi" tampaknya terlalu rentan terhadap kesalahan.

Saya ingin menyesuaikan skema identitas aspnet, dan tidak diganggu dengan migrasi. Saya berpengalaman dengan proyek database Visual Studio (sqlpackage, data-dude) dan bagaimana ia melakukan pekerjaan yang cukup baik dalam meningkatkan skema.

Solusi sederhana saya adalah:

1) Buat proyek database yang mencerminkan skema identitas aspnet 2) gunakan keluaran proyek ini (.dacpac) sebagai sumber daya proyek 3) terapkan .dacpac bila diperlukan

Untuk MVC5, mengubah ApplicationDbContextkelas tampaknya membuat ini berjalan ...

1) Terapkan IDatabaseInitializer

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

2) Dalam konstruktor, beri tanda bahwa kelas ini akan mengimplementasikan inisialisasi database:

Database.SetInitializer<ApplicationDbContext>(this);

3) Terapkan InitializeDatabase:

Di sini, saya memilih untuk menggunakan DacFX dan menerapkan .dacpac saya

    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);
            }
        }
    }
Aaron Hudon
sumber
2

Saya menghabiskan beberapa jam mengerjakan ini dan akhirnya menemukan solusi yang telah saya bagikan di blog saya di sini . Pada dasarnya, Anda perlu melakukan semua yang dikatakan dalam jawaban stink tetapi dengan satu hal tambahan: memastikan Identity Framework memiliki string koneksi SQL-Client tertentu di atas string koneksi Entity Framework yang digunakan untuk entitas aplikasi Anda.

Singkatnya, aplikasi Anda akan menggunakan string koneksi untuk Identity Framework dan satu lagi untuk entitas aplikasi Anda. Setiap string koneksi memiliki tipe yang berbeda. Baca posting blog saya untuk tutorial lengkap.

Daniel Eagle
sumber
Saya mencoba semua yang Anda sebutkan di blog Anda dua kali, itu tidak berhasil untuk saya.
Badhon Jain
@Badhon Saya sangat menyesal instruksi dalam posting blog saya tidak berhasil untuk Anda. Saya memiliki banyak orang yang mengucapkan terima kasih karena mereka telah menemukan kesuksesan dengan mengikuti artikel saya. Ingatlah selalu bahwa jika Microsoft memperbarui sesuatu, itu dapat mempengaruhi hasilnya. Saya menulis artikel untuk ASP.NET MVC 5 dengan Identity Framework 2.0. Apa pun di luar itu mungkin mengalami masalah, tetapi sejauh ini saya telah menerima komentar terbaru yang menunjukkan keberhasilan. Saya ingin mendengar lebih banyak tentang masalah Anda.
Daniel Eagle
Saya akan mencoba mengikuti sekali lagi, masalah yang saya hadapi adalah saya tidak dapat menggunakan database kustom saya, itu menggunakan database yang dibuat secara otomatis. Bagaimanapun, saya tidak bermaksud mengatakan sesuatu yang salah dengan artikel Anda. Terima kasih telah membagikan pengetahuan Anda.
Badhon Jain
1
@Badhon Jangan khawatir teman saya, saya tidak pernah merasa Anda mengatakan ada yang salah dengan artikel itu. Kita semua memiliki situasi unik dengan berbagai kasus tepi, jadi terkadang apa yang berhasil untuk orang lain belum tentu berhasil untuk kita. Saya harap Anda dapat menyelesaikan masalah Anda.
Daniel Eagle
2

Saya menemukan bahwa @ JoshYates1980 memang memiliki jawaban yang paling sederhana.

Setelah serangkaian uji coba dan kesalahan saya melakukan apa yang disarankan Josh dan menggantinya connectionStringdengan string koneksi DB yang saya hasilkan. yang membuat saya bingung awalnya adalah postingan berikut:

Cara menambahkan otentikasi identitas ASP.NET MVC5 ke database yang ada

Dimana jawaban yang diterima dari @Win menyatakan untuk mengubah ApplicationDbContext()nama koneksi. Ini agak kabur jika Anda menggunakan Entitas dan pendekatan pertama Database / Model di mana string koneksi database dibuat dan ditambahkan ke Web.configfile.

Nama ApplicationDbContext()koneksi dipetakan ke koneksi default di Web.configfile. Oleh karena itu, metode Josh bekerja paling baik, tetapi untuk membuatnya ApplicationDbContext()lebih mudah dibaca, saya sarankan mengubah nama ke nama database Anda sebagai @Win yang awalnya diposting, pastikan untuk mengubah connectionStringuntuk "DefaultConnection" di Web.configdan mengomentari dan / atau menghapus Entitas database yang dihasilkan termasuk.

Contoh Kode:

TheSchnitz
sumber
1

Kami memiliki proyek DLL Model Entitas tempat kami menyimpan kelas model kami. Kami juga menyimpan Proyek Database dengan semua skrip database. Pendekatan saya adalah sebagai berikut

1) Buat proyek Anda sendiri yang memiliki EDMX menggunakan database terlebih dahulu

2) Buat skrip tabel di db Anda, saya menggunakan VS2013 yang terhubung ke localDB (Koneksi Data) dan menyalin skrip ke proyek database, menambahkan kolom kustom apa pun, misalnya Tanggal Lahir [DATE] bukan null

3) Terapkan database

4) Perbarui proyek Model (EDMX) Tambahkan ke proyek Model

5) Tambahkan kolom kustom ke kelas aplikasi

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

Dalam proyek MVC, AccountController menambahkan yang berikut ini:

Penyedia Identitas menginginkan SQL Connection String agar berfungsi, untuk menyimpan hanya 1 string koneksi untuk database, ekstrak string penyedia dari string koneksi 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
sumber