ASP.NET Identity with EF Database First MVC5


88

Có thể sử dụng Asp.net Identity mới với Database First và EDMX không? Hay chỉ với mã đầu tiên?

Đây là những gì tôi đã làm:

1) Tôi đã tạo một Dự án MVC5 mới và có Identity mới tạo bảng Người dùng và Vai trò mới trong cơ sở dữ liệu của tôi.

2) Sau đó, tôi mở tệp Cơ sở dữ liệu đầu tiên EDMX của mình và kéo vào bảng Người dùng danh tính mới vì tôi có các bảng khác liên quan đến nó.

3) Khi lưu EDMX, trình tạo POCO trên Cơ sở dữ liệu sẽ tự động tạo một lớp Người dùng. Tuy nhiên, UserManager và RoleManager mong đợi một lớp Người dùng kế thừa từ không gian tên Identity mới (Microsoft.AspNet.Identity.IUser), vì vậy việc sử dụng lớp Người dùng POCO sẽ không hoạt động.

Tôi đoán một giải pháp khả thi là chỉnh sửa Lớp thế hệ POCO của tôi để lớp Người dùng của tôi kế thừa từ IUser?

Hay ASP.NET Identity chỉ tương thích với Code First Design?

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

Cập nhật: Theo gợi ý của Anders Abel bên dưới, đây là những gì tôi đã làm. Nó hoạt động, nhưng tôi tự hỏi nếu có một giải pháp thanh lịch hơn.

1) Tôi đã mở rộng thực thể Lớp người dùng của mình bằng cách tạo một lớp một phần trong cùng không gian tên với các thực thể được tạo tự động của tôi.

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

2) Tôi đã thay đổi DataContext của mình để kế thừa từ IdentityDBContext thay vì DBContext. Lưu ý rằng mỗi khi bạn cập nhật EDMX của mình và tạo lại các lớp DBContext và Entity, bạn sẽ phải đặt điều này trở lại.

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

3) Trong lớp thực thể Người dùng được tạo tự động của bạn, bạn phải thêm từ khóa ghi đè vào 4 trường sau hoặc nhận xét các trường này vì chúng được kế thừa từ IdentityUser (Bước 1). Lưu ý rằng mỗi khi bạn cập nhật EDMX của mình và tạo lại các lớp DBContext và Entity, bạn sẽ phải đặt điều này trở lại.

    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
bạn có mã mẫu triển khai của bạn không? như khi tôi cố gắng sao chép ở trên, tôi gặp lỗi khi cố gắng đăng nhập hoặc đăng ký người dùng. "Loại thực thể AspNetUser không phải là một phần của mô hình cho ngữ cảnh hiện tại" trong đó AspNetUser là thực thể Người dùng của tôi
Tim

Bạn đã thêm bảng AspNetUser vào EDMX của mình chưa? Ngoài ra, hãy đảm bảo rằng AccountController của bạn đang sử dụng MVC5Test_DBEntities (hoặc bất kỳ ngữ cảnh DB nào của bạn được đặt tên) thay vì ApplicationContext.
Patrick Tran

8
ASP.NET Identity là một đống hấp dẫn của ____. Hỗ trợ khủng khiếp cho cơ sở dữ liệu đầu tiên, không có tài liệu, các ràng buộc tham chiếu kém (thiếu TRÊN CASCADE DELETE trên máy chủ SQL) và sử dụng chuỗi cho ID (vấn đề hiệu suất và phân mảnh chỉ mục). Và đây là nỗ lực 297 của họ tại khuôn khổ bản sắc ...
DeepSpace101

1
@ DeepSpace101 Identity hỗ trợ DB-first giống như Code-first. Các mẫu được thiết lập để thực hiện mã trước vì vậy nếu bạn bắt đầu từ một mẫu, bạn phải thay đổi một số thứ xung quanh. Xóa tầng hoạt động tốt, bạn có thể thay đổi chuỗi thành int dễ dàng. Xem câu trả lời của tôi dưới đây.
Giày

1
@Shoe Tôi phải nói rằng tôi nghĩ rằng bạn có thể sai. Tôi vẫn chưa tìm thấy một ví dụ / hướng dẫn toàn diện và hiệu quả về cách triển khai điều này trong db-first (không có tài liệu). API cố gắng tham chiếu đến bảng nối "IdentityUserRoles" thông qua thuộc tính IdentityUser.Roles, điều này phá vỡ mối quan hệ trên EF db-first vì bảng nối không được hiển thị dưới dạng thực thể (ràng buộc tham chiếu kém-ish). Tôi không đồng ý về các chuỗi cho ID vì điều đó có thể được tùy chỉnh bằng cách chỉ định các tham số kiểu trong các lớp kế thừa. Tóm lại, với tôi, có vẻ như họ không nghĩ đến DB trước.
Lopsided vào

Câu trả lời:


16

Có thể sử dụng hệ thống nhận dạng với POCO và Database First, nhưng bạn sẽ phải thực hiện một vài chỉnh sửa:

  1. Cập nhật tệp .tt để tạo POCO để tạo các lớp thực thể partial. Điều đó sẽ giúp bạn có thể cung cấp triển khai bổ sung trong một tệp riêng biệt.
  2. Thực hiện một phần triển khai của Userlớp trong một tệp khác

 

partial User : IUser
{
}

Điều đó sẽ làm cho Userlớp triển khai giao diện phù hợp, mà không chạm vào các tệp được tạo thực tế (chỉnh sửa tệp được tạo luôn là một ý tưởng tồi).


Cảm ơn. Tôi sẽ phải thử điều này và báo cáo nếu nó hoạt động.
Patrick Tran

Tôi đã thử những gì bạn đề cập. Xem bài đăng của tôi để biết thông tin cập nhật ... nhưng giải pháp không được tốt cho lắm: /
Patrick Tran

Tôi đã gặp vấn đề tương tự. Có vẻ như tài liệu về DB-first rất thưa thớt. Đây là một gợi ý tốt đẹp, nhưng tôi nghĩ rằng bạn nói đúng, nó không hoàn toàn làm việc
Phil

4
Có giải pháp nào không phải là hack không?
user20358

Tôi đang sử dụng Kiến trúc Onion và tất cả POCO đều nằm trong Core. Không khuyến khích sử dụng IUser. Vậy giải pháp nào khác?
Usman Khalid

13

Các bước của tôi rất giống nhau nhưng tôi muốn chia sẻ.

1) Tạo một dự án MVC5 mới

2) Tạo một Model mới.edmx. Ngay cả khi đó là một cơ sở dữ liệu mới và không có bảng.

3) Chỉnh sửa web.config và thay thế chuỗi kết nối được tạo này:

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

với kết nối này:

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

Sau đó, xây dựng và chạy ứng dụng. Đăng ký một người dùng và sau đó các bảng sẽ được tạo.


1
điều này đã giải quyết được sự cố, tôi không thể nhìn thấy người dùng ứng dụng trong cơ sở dữ liệu, nhưng sau khi thay thế kết nối mặc định, nó đã hoạt động.
Hassen Ch.

10

CHỈNH SỬA: Nhận dạng ASP.NET với Cơ sở dữ liệu EF Đầu tiên cho Mẫu dự án MVC5 CodePlex.


Tôi muốn sử dụng cơ sở dữ liệu hiện có và tạo mối quan hệ với ApplicationUser. Đây là cách tôi đã thực hiện bằng cách sử dụng SQL Server nhưng ý tưởng tương tự có thể sẽ hoạt động với bất kỳ DB nào.

  1. Tạo một dự án MVC
  2. Mở DB được liệt kê trong DefaultConnection trong Web.config. Nó sẽ được gọi là (aspnet- [timestamp] hoặc một cái gì đó tương tự.)
  3. Tập lệnh cho các bảng cơ sở dữ liệu.
  4. Chèn các bảng đã tập lệnh vào cơ sở dữ liệu hiện có trong SQL Server Management Studio.
  5. Tùy chỉnh và thêm các mối quan hệ vào ApplicationUser (nếu cần).
  6. Tạo Dự án web mới> MVC> Dự án đầu tiên của DB> Nhập DB với EF ... Loại trừ các Lớp nhận dạng bạn đã chèn.
  7. Trong IdentityModels.cs, thay đổi ApplicationDbContext :base("DefaltConnection")để sử dụng DbContext trong dự án của bạn.

Chỉnh sửa: Sơ đồ lớp nhận dạng Asp.Net nhập mô tả hình ảnh ở đây


6
Vấn đề không phải là DBContext, nhưng điều đó UserManager và RoleManager mong đợi một lớp kế thừa từ Microsoft.AspNet.Identity.EntityFramework.IdentityUser
Patrick Trần

public class IdentityDbContext <TUser>: DbContext trong đó TUser: Microsoft.AspNet.Identity.EntityFramework.IdentityUser. Khi sử dụng cơ sở dữ liệu đầu tiên, các lớp thực thể được tạo không kế thừa từ bất kỳ lớp cơ sở nào.
Patrick Tran

Sau đó, chỉ cần loại trừ chúng khỏi cơ sở dữ liệu khi bạn tạo các lớp với khung thực thể.
bốc mùi vào

Nếu bạn loại trừ bảng Identity khỏi EDMX, thì bạn sẽ mất các thuộc tính Navigation trong các lớp khác có khóa ngoại đối với UserID của bạn
Patrick Tran

34
Tôi không miễn cưỡng chuyển sang viết mã trước ... Chỉ là trong một số trường hợp và công ty, quản trị viên db tạo ra các bảng cơ sở chứ không phải lập trình viên.
Patrick Tran

8

IdentityUserở đây là vô giá trị bởi vì nó là đối tượng đầu tiên mã được sử dụng UserStoređể xác thực. Sau khi xác định Userđối tượng của riêng mình , tôi đã triển khai một lớp một phần có chức năng triển khai IUserđược lớp đó sử dụng UserManager. Tôi muốn Ids của tôi intthay vì chuỗi vì vậy tôi chỉ trả về UserID của toString (). Tương tự như vậy tôi muốn nUsernamelà 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;
        }
    }
}

Bạn hoàn toàn không cần IUser. Nó chỉ là một giao diện được sử dụng bởi UserManager. Vì vậy, nếu bạn muốn xác định một "IUser" khác, bạn sẽ phải viết lại lớp này để sử dụng việc triển khai của riêng bạn.

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

Bây giờ, bạn viết của riêng bạn UserStore, xử lý tất cả lưu trữ của người dùng, xác nhận quyền sở hữu, vai trò, v.v. Triển khai các giao diện của mọi thứ mà người dùng mã UserStorethực hiện đầu tiên và thay đổi where TUser : IdentityUserthành where TUser : Usernơi "Người dùng" là đối tượng thực thể của bạn

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
}

Dưới đây là một số ví dụ về một số triển khai giao diện

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

Sử dụng mẫu MVC 5, tôi đã thay đổi AccountControllergiao diện như thế này.

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

Bây giờ đăng nhập sẽ hoạt động với các bảng của riêng bạn.


1
Gần đây tôi đã có cơ hội thực hiện điều này và vì một số lý do (tôi tin rằng do bản cập nhật 3.0 của danh tính) tôi không thể triển khai đăng nhập bằng cách kế thừa IdentityUser và sau đó ghi đè các thuộc tính; nhưng việc viết một UserStore tùy chỉnh và kế thừa IUser hoạt động tốt; Chỉ cần đưa ra một bản cập nhật, có thể ai đó thấy điều này hữu ích.
Naz Ekin

Bạn có thể cung cấp liên kết cho tất cả việc triển khai giao diện hoặc cho toàn bộ dự án không?
DespeiL

có một lý do cụ thể tại sao bạn sử dụng một phần lớp ở đây?
user8964654

Nếu bạn đang sử dụng edmx để tạo các mô hình của mình, bạn phải sử dụng một lớp từng phần. Nếu bạn đang làm mã trước thì bạn có thể bỏ qua điều này
Shoe


3

Câu hỏi hay.

Tôi là người ưu tiên cơ sở dữ liệu hơn. Mô hình đầu tiên của mã có vẻ hơi ngớ ngẩn đối với tôi, và "di chuyển" dường như quá dễ xảy ra lỗi.

Tôi muốn tùy chỉnh lược đồ nhận dạng aspnet và không bị làm phiền với việc di chuyển. Tôi thành thạo với các dự án cơ sở dữ liệu Visual Studio (sqlpackage, data-dude) và cách nó hoạt động khá tốt trong việc nâng cấp các lược đồ.

Giải pháp đơn giản của tôi là:

1) Tạo một dự án cơ sở dữ liệu phản chiếu giản đồ nhận dạng aspnet 2) sử dụng đầu ra của dự án này (.dacpac) làm tài nguyên dự án 3) triển khai .dacpac khi cần

Đối với MVC5, việc sửa đổi ApplicationDbContextlớp dường như sẽ diễn ra ...

1) Thực hiện IDatabaseInitializer

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

2) Trong hàm tạo, báo hiệu rằng lớp này sẽ thực hiện khởi tạo cơ sở dữ liệu:

Database.SetInitializer<ApplicationDbContext>(this);

3) Thực hiện InitializeDatabase:

Ở đây, tôi đã chọn sử dụng DacFX và triển khai .dacpac của mình

    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

Tôi đã dành vài giờ để giải quyết vấn đề này và cuối cùng đã tìm ra giải pháp mà tôi đã chia sẻ trên blog của mình tại đây . Về cơ bản, bạn cần phải làm mọi thứ đã nói trong câu trả lời của stink nhưng với một điều bổ sung: đảm bảo Identity Framework có một chuỗi kết nối SQL-Client cụ thể trên đầu chuỗi kết nối Entity Framework được sử dụng cho các thực thể ứng dụng của bạn.

Tóm lại, ứng dụng của bạn sẽ sử dụng một chuỗi kết nối cho Khung danh tính và một chuỗi khác cho các thực thể ứng dụng của bạn. Mỗi chuỗi kết nối là một kiểu khác nhau. Đọc bài đăng trên blog của tôi để có hướng dẫn đầy đủ.


Tôi đã thử mọi thứ bạn đề cập trong blog của bạn hai lần, nó không hiệu quả với tôi.
Badhon Jain

@Badhon Tôi rất tiếc vì hướng dẫn trong bài đăng trên blog của tôi không phù hợp với bạn. Tôi đã có vô số người bày tỏ sự cảm ơn của họ vì họ đã tìm thấy thành công sau bài viết của tôi. Luôn nhớ rằng nếu Microsoft cập nhật thứ gì đó, nó có thể ảnh hưởng đến kết quả. Tôi đã viết bài báo cho ASP.NET MVC 5 với Identity Framework 2.0. Bất cứ điều gì vượt quá điều đó có thể gặp vấn đề nhưng cho đến nay tôi đã nhận được những nhận xét rất gần đây cho thấy sự thành công. Tôi muốn nghe thêm về vấn đề của bạn.
Daniel Eagle

Tôi sẽ cố gắng làm theo một lần nữa, vấn đề tôi gặp phải là tôi không thể sử dụng cơ sở dữ liệu tùy chỉnh của mình, nó đã sử dụng cơ sở dữ liệu được xây dựng tự động. Dù sao, tôi không cố ý nói điều gì sai với bài viết của bạn. Cảm ơn vì đã chia sẻ kiến ​​thức của bạn.
Badhon Jain

1
@Badhon Đừng lo lắng, bạn của tôi, tôi chưa bao giờ cảm thấy bạn nói điều gì sai với bài báo. Tất cả chúng ta đều có những tình huống riêng biệt với nhiều trường hợp khác nhau, vì vậy đôi khi những gì phù hợp với người khác có thể không nhất thiết phải hiệu quả với chúng ta. Tôi hy vọng bạn có thể giải quyết vấn đề của mình.
Daniel Eagle

2

Tôi thấy rằng @ JoshYates1980 có câu trả lời đơn giản nhất.

Sau một loạt thử nghiệm và gặp lỗi, tôi đã làm theo những gì Josh đề xuất và thay thế connectionStringbằng chuỗi kết nối DB đã tạo của tôi. điều tôi bối rối ban đầu là bài đăng sau:

Cách thêm Xác thực danh tính ASP.NET MVC5 vào cơ sở dữ liệu hiện có

Trường hợp câu trả lời được chấp nhận từ @Win đã nêu để thay đổi ApplicationDbContext()tên kết nối. Điều này hơi mơ hồ nếu bạn đang sử dụng Phương pháp tiếp cận đầu tiên đối với Thực thể và Cơ sở dữ liệu / Mô hình trong đó chuỗi kết nối cơ sở dữ liệu được tạo và thêm vào Web.configtệp.

Tên ApplicationDbContext()kết nối được ánh xạ tới kết nối mặc định trong Web.configtệp. Do đó, phương pháp của Josh hoạt động tốt nhất, nhưng để ApplicationDbContext()dễ đọc hơn, tôi khuyên bạn nên thay đổi tên thành tên cơ sở dữ liệu của bạn như @Win đã đăng ban đầu, đảm bảo thay đổi connectionStringcho "DefaultConnection" trong Web.configvà nhận xét và / hoặc xóa Đối tượng cơ sở dữ liệu được tạo bao gồm.

Ví dụ về mã:


1

Chúng tôi có một dự án DLL Mô hình Thực thể nơi chúng tôi giữ lớp mô hình của mình. Chúng tôi cũng giữ một Dự án Cơ sở dữ liệu với tất cả các tập lệnh cơ sở dữ liệu. Cách tiếp cận của tôi như sau

1) Tạo dự án của riêng bạn có EDMX bằng cách sử dụng cơ sở dữ liệu trước tiên

2) Tập lệnh cho các bảng trong db của bạn, tôi đã sử dụng VS2013 được kết nối với localDB (Kết nối dữ liệu) và sao chép tập lệnh vào dự án cơ sở dữ liệu, thêm bất kỳ cột tùy chỉnh nào, ví dụ: Ngày sinh [DATE] không rỗng

3) Triển khai cơ sở dữ liệu

4) Cập nhật dự án Mô hình (EDMX) Thêm vào dự án Mô hình

5) Thêm bất kỳ cột tùy chỉnh nào vào lớp ứng dụng

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

Trong dự án MVC AccountController đã thêm những thứ sau:

Nhà cung cấp danh tính muốn một Chuỗi kết nối SQL để nó hoạt động, chỉ giữ lại 1 chuỗi kết nối cho cơ sở dữ liệu, hãy trích xuất chuỗi nhà cung cấp từ chuỗi kết nối 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)));
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.