2012-09-25 6 views
24

Có thể SimpleMembership được sử dụng với EF model-first? Khi tôi dùng thử, tôi nhận được "Không thể tìm thấy Nhà cung cấp Dữ liệu Khuôn khổ .NET được yêu cầu" khi tôi gọi WebSecurity.InitializeDatabaseConnection.Sử dụng SimpleMembership với EF model-first

Để đặt theo cách khác: Tôi không thể gọi đến số WebSecurity.InitializeDatabaseConnection để hoạt động khi chuỗi kết nối sử dụng System.Data.EntityClient nhà cung cấp (giống như khi sử dụng mô hình mô hình đầu tiên mô hình đầu tiên).

Để repro cố, hãy tạo một ứng dụng MVC 4, và thay thế UserProfile lớp thực thể mã đầu tiên (mà bạn nhận được miễn phí với MVC 4 mẫu) với một lớp tài mô hình đầu tiên mà bạn có tạo ra trong thiết kế Entity:

  1. Tạo một ứng dụng MVC 4 trong VS 2012 và thêm một mới, trống Entity Data Model.
  2. Thêm đối tượng mới có tên User vào kiểu máy, với các trường dành cho Id, UserName, and FullName. Vì vậy, tại thời điểm này, thực thể dữ liệu User là ánh xạ tới bảng Users và được truy cập thông qua một kết nối funky chuỗi sử dụng nhà cung cấp System.Data.EntityClient.
  3. Xác minh rằng EF có thể truy cập thực thể User. Một cách dễ dàng để thực hiện là để dàn dựng ra một bộ điều khiển Người dùng dựa trên bảng Người dùng và DbContext liên quan của nó.
  4. Chỉnh sửa tệp AccountModels.cs để xóa lớp UserProfile và lớp UsersContext được liên kết của nó. Thay thế các tham chiếu đến các lớp (hiện bị thiếu) UserProfileUsersContext với các tham chiếu đến lớp Người dùng mới của bạn và lớp học DbContext được liên kết của nó.
  5. Chuyển cuộc gọi đến InitializeDatabaseConnection từ lớp InitializeSimpleMembershipAttribute filter thành phương thức Application_Start trong Global.asax.cs. Khi bạn đang ở đó, hãy sửa đổi các đối số để sử dụng kết nối của đối tượng Người dùng mới chuỗi, tên bảng và tên cột UserId của bạn.
  6. Xóa lớp (không còn được sử dụng) InitializeSimpleMembershipAttribute và tham chiếu đến nó.

Khi bạn chạy repro, nó sẽ nhận được một ngoại lệ tại cuộc gọi đến InitializeDatabaseConnection.

Bob

+1

Bạn có nghĩa là thay thế công khai DbSet UserProfiles {get; bộ; } với (public DbSet userProfiles {get; set;}). Tôi là loại trong một sửa chữa với điều này –

+1

@PeterEdike Có, đó là những gì tôi đã cố gắng để làm. Tôi đã cố gắng thay thế tất cả các công cụ đầu tiên bằng mã với công cụ đầu tiên của mình, nhưng tôi phát hiện ra rằng SimpleMembership không hoạt động với ma thuật mô hình đầu tiên trong chuỗi kết nối. Như Mario Zderic nói, giải pháp là sử dụng một cái gì đó trông giống như chuỗi kết nối mã đầu tiên. –

Trả lời

24

SimpleMembership có thể làm việc với mô hình trước tiên. Đây là giải pháp.

1. InitializeSimpleMembershipAttribute.cs từ MVC 4 ứng dụng Internet templete sẽ trông như thế này

namespace WebAndAPILayer.Filters 
{ 
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
    public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute 
    { 
     private static SimpleMembershipInitializer _initializer; 
     private static object _initializerLock = new object(); 
     private static bool _isInitialized; 

     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      // Ensure ASP.NET Simple Membership is initialized only once per app start 
      LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock); 
     } 

     private class SimpleMembershipInitializer 
     { 
      public SimpleMembershipInitializer() 
      { 
       try 
       { 
        WebSecurity.InitializeDatabaseConnection("ConnStringForWebSecurity", "UserProfile", "Id", "UserName", autoCreateTables: true); 
       } 
       catch (Exception ex) 
       { 
        throw new InvalidOperationException("Something is wrong", ex); 
       } 
      } 
     } 
    } 
} 

Lớp 2.Delete CodeFirst từ AcountModel.cs

3.Fix AccountCotroler.cs để làm việc với DbContext bạn Model-đầu tiên (ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl) phương pháp)

4.Define "ConnStringForWebSecurity" kết nối chuỗi của bạn mà không phải là giống như chuỗi conn sôi nổi để truy cập db mô hình đầu tiên, thông báo rằng chúng tôi sử dụng nhà cung cấp System.Data.SqlClient không System.Data.EntityClient

<connectionStrings> 
     <add name="ModelFirstEntityFramework" connectionString="metadata=res://*/Context.csdl|res://*/Context.ssdl|res://*/Context.msl;provider=System.Data.SqlClient;provider 
connection string=&quot;data source=.\SQLEXPRESS;Initial 
Catalog=aspnet-MVC4;Integrated 
Security=SSPI;multipleactiveresultsets=True;App=EntityFramework&quot;" 
providerName="System.Data.EntityClient" /> 
     <add name="ConnStringForWebSecurity" connectionString="data source=.\SQLEXPRESS;Initial Catalog=aspnet-MVC4;Integrated 
Security=SSPI" providerName="System.Data.SqlClient" /> 
     </connectionStrings> 
+2

Cảm ơn bạn Mario! Điều đó đặt ra một vài câu hỏi về cách hai kết nối DbContext hợp tác với nhau. Tôi sẽ tạo chủ đề diễn đàn mới cho hai câu hỏi đó. –

+2

Các chuỗi kết nối kép là phần tôi bị thiếu trong DbFirst! Vì vậy, các trình trợ giúp SimpleMembershipProvider chỉ cần làm việc với các chuỗi kết nối cổ điển chứ không phải các chuỗi kết nối EF. – profMamba

+1

Tôi mới đến với thế giới MVC, và bây giờ tôi có cùng một vấn đề ..., ý bạn là gì ở điểm 2, 3, xin bạn có thể giải thích tốt hơn cho những người mới như tôi, cảm ơn trước. –

12

Đó là một lỗi trong MVC 4. There's a workaround in this blog post.

Là một bộ lọc hành động, InitializeSimpleMembershipAttribute móc vào OnActionExecuting để thực hiện các công việc khởi tạo lười biếng, nhưng điều này có thể là quá muộn trong vòng đời. Thuộc tính Authorize sẽ cần các nhà cung cấp sẵn sàng sớm hơn nếu nó cần thực hiện kiểm tra truy cập dựa trên vai trò (trong OnAuthorization).Nói cách khác, nếu yêu cầu đầu tiên đến một trang web lượt truy cập một hành động điều khiển như sau:

[Authorize(Roles="Sales")] 

.. sau đó bạn sẽ có một ngoại lệ như các bộ lọc kiểm tra vai trò của người dùng nhưng các nhà cung cấp aren không được khởi tạo.

Đề xuất của tôi là xóa ISMA khỏi dự án và khởi tạo WebSecurity trong sự kiện bắt đầu ứng dụng.

+0

Cảm ơn Craig. Khi tôi lần đầu tiên bắt tay vào nhiệm vụ của mình để hợp nhất các thực thể mẫu đầu tiên với ma thuật SimpleMembership, trên thực tế, tôi đã loại bỏ ISMA và chuyển cuộc gọi đến InitializeDatabaseConnection từ trình khởi tạo lười đến phương thức Application_Start trong Global.asax.cs. Tôi chỉ không muốn làm phức tạp kịch bản trong bài đăng gốc của tôi với chi tiết đó. Ngoại lệ "không thể tìm thấy nhà cung cấp" xảy ra bất cứ khi nào tôi cố gắng kết hợp thực thể kiểu đầu tiên với SimpleMembership, cho dù tôi thực hiện cuộc gọi trong trình khởi tạo lười hay trong Application_Start. –

+1

Vâng, giải pháp với 30 dòng mã tiêu cực! Tôi thích nó. +1 –

10

1 - Bạn cần phải bật di cư, prefereably với EntityFramework 5

2 - Di chuyển của bạn

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true); 

phương pháp Seed bạn trong lớp YourMvcApp/Migrations/Configuration.cs bạn

protected override void Seed(UsersContext context) 
    { 
     WebSecurity.InitializeDatabaseConnection(
      "DefaultConnection", 
      "UserProfile", 
      "UserId", 
      "UserName", autoCreateTables: true); 

     if (!Roles.RoleExists("Administrator")) 
      Roles.CreateRole("Administrator"); 

     if (!WebSecurity.UserExists("lelong37")) 
      WebSecurity.CreateUserAndAccount(
       "lelong37", 
       "password", 
       new {Mobile = "+19725000000", IsSmsVerified = false}); 

     if (!Roles.GetRolesForUser("lelong37").Contains("Administrator")) 
      Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"}); 
    } 

Bây giờ EF5 sẽ chịu trách nhiệm tạo bảng UserProfile của bạn, sau khi thực hiện xong, bạn sẽ sẽ gọi WebSecurity.InitializeDatabaseConnection để chỉ đăng ký SimpleMembershipProvider với bảng UserProfile đã tạo (Trong trường hợp của bạn, bạn có thể thay thế giá trị tham số "UserProfile" bằng tên bảng tùy chỉnh của mình), cũng biết SimpleMembershipProvider cột nào là UserId và UserName. Tôi cũng hiển thị một ví dụ về cách bạn có thể thêm Người dùng, Vai trò và liên kết hai trong phương thức Seed của bạn với các thuộc tính/trường tùy chỉnh UserProfile ví dụ: Số điện thoại di động của người dùng.

3 - Bây giờ khi bạn chạy cập nhật cơ sở dữ liệu từ Package Manager Console, EF5 sẽ cung cấp bảng của bạn với tất cả các thuộc tính tùy chỉnh của bạn

Đối với tài liệu tham khảo thêm xin vui lòng tham khảo bài viết này với sourcecode: http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/

+0

Cảm ơn Long Le. Thật không may, vấn đề của tôi là tôi đang sử dụng "model-first", chứ không phải "code-first". Theo tôi biết (sửa tôi nếu tôi sai), tính năng "di chuyển" chỉ áp dụng cho mã đầu tiên, không áp dụng cho kiểu đầu tiên. Nhưng các ví dụ mã để tạo vai trò và người dùng sẽ rất hữu ích khi tôi nhận được claptrap cơ bản hoạt động! - Bob –

+1

Bob.as.SBS - tôi rất vui vì ít nhất một điều gì đó trong câu trả lời của tôi rất hữu ích cho bạn, bạn có phiền khi đưa ra câu trả lời này vì nó có hỗ trợ cho bạn không? - cảm ơn – LeLong37

+0

Nếu tôi có thể. Tôi không có đủ "danh tiếng" để bỏ phiếu. –

0

Tôi không thể làm việc với lớp WebSecurity của EF và WebMatrix để tránh vấn đề này và tiếp tục:

Thay đổi mô hình Ef đầu tiên của tôi thành mã đầu tiên.

Thay đổi chuỗi kết nối để sử dụng providerName = "System.Data.SqlClient" (xóa tất cả các thông tin siêu dữ liệu) hoặc sử dụng kết nối EF

Trong trường hợp của tôi mô hình, dữ liệu và web là proyects khác nhau vì vậy cho tôi không phải là vấn đề để xóa thông tin này khỏi web.config trên web.project.

Ngày nay websecuroty.initializedatabase không chạy với chuỗi kết nối EF.

Tôi muốn điều này giúp

1

sự cố này gây ra bởi WebSecurity.InitializeDatabaseConnection không thể sử dụng chuỗi kết nối với tên System.Data.EntityClient nhà cung cấp.

cung cấp chuỗi kết nối kép không tốt, vì vậy bạn có thể tạo chuỗi kết nối cho mô hình EF đầu tiên trong hàm tạo trong lớp một phần.

mã được trông giống như dưới đây

public partial class MyDataContext 
{ 
    private static string GenerateConnectionString(string connectionString) 
    { 
     var cs = System.Configuration.ConfigurationManager 
        .ConnectionStrings[connectionString]; 

     SqlConnectionStringBuilder sb = 
      new SqlConnectionStringBuilder(cs.ConnectionString); 
     EntityConnectionStringBuilder builder = 
      new EntityConnectionStringBuilder(); 
     builder.Provider = cs.ProviderName; 
     builder.ProviderConnectionString = sb.ConnectionString; 
     builder.Metadata = "res://*/MyDataContext.csdl|" + 
       "res://*/MyDataContext.ssdl|res://*/MyDataContext.msl"; 
     return builder.ToString(); 
    } 

    public MyDataContext(string connectionName) : 
      base(GenerateConnectionString(connectionName)) { } 
} 

với thủ thuật này bạn có thể sử dụng chuỗi kết nối duy nhất trên cấu hình web của bạn, nhưng một trong những vấn đề bạn không thể sử dụng constructor mặc định trên DataContext của bạn, thay vào đó bạn nên gieo rắc kết nối tên chuỗi ở khắp mọi nơi khi bạn khởi tạo datacontext. nhưng nó không phải là một vấn đề lớn khi bạn sử dụng mô hình tiêm phụ thuộc.