2013-04-09 25 views
8

Gần đây, tôi đã thực hiện các thay đổi đối với Ứng dụng MVC3 của mình khi cố gắng xử lý đúng các đối tượng DbContext [1]. Điều này làm việc rất lớn trong phát triển, nhưng một khi ứng dụng đã được đẩy đến máy chủ sản xuất của tôi, tôi bắt đầu liên tục nhận được một số ngoại lệ vui mà sẽ tồn tại cho đến khi AppPool được tái chế. Các trường hợp ngoại lệ có thể được truy trở lại mã trong tùy chỉnh của tôi AuthorizeAttribute và trông giống như:Các vấn đề sau khi vứt bỏ DbContext

System.InvalidOperationException: The 'Username' property on 'User' could not be set to a 'Int32' value. You must set this property to a non-null value of type 'String'. 

System.InvalidOperationException: The 'Code' property on 'Right' could not be set to a 'String' value. You must set this property to a non-null value of type 'Int32'. 

(schema Cơ sở dữ liệu trông như thế này: Người dùng: [Guid, String, ...], Quyền: [Guid, Int32,. ..])

Giống như một số "dây đang bị vượt qua" và ứng dụng đang trộn các kết quả từ cơ sở dữ liệu: cố gắng thực hiện kết quả Right dưới dạng User và ngược lại.

Để quản lý việc xử lý DbContext, tôi đặt mã vào để lưu trữ điều này ở cấp độ trên mỗi bộ điều khiển. Khi bộ điều khiển được xử lý, tôi cũng loại bỏ DbContext. Tôi biết nó là hacky, nhưng AuthorizeAttribute sử dụng cùng một ngữ cảnh thông qua filterContext.Controller.

Có điều gì sai khi xử lý vòng đời đối tượng DbContext trong trang viên này không? Có bất kỳ giải thích hợp lý nào về lý do tại sao tôi nhận được các trường hợp ngoại lệ chéo trên không?

[1] Mặc dù tôi hiểu rằng không cần thiết phải vứt bỏ các đối tượng DbContext, gần đây tôi đã gặp một số nguồn cho biết đó là phương pháp hay nhất bất kể.

Chỉnh sửa (mỗi @ bình luận MikeSW của)

Một tài sản của AuthorizeAttribute đại diện cho DbContext đang được thiết lập trong phương pháp OnAuthorization, khi AuthorizationContext là trong phạm vi. Thuộc tính này sau đó được sử dụng theo phương pháp AuthorizeCore.

+0

Bạn có thể chia sẻ một số mã có liên quan của AuthorizeAttribute tùy chỉnh của mình không? Lưu ý rằng một thuộc tính được sử dụng như một singleton bởi asp.net mvc. Ngoài ra, bạn có đang sử dụng DI Container không? – MikeSW

+0

@MikeSW Tôi đã thêm thông tin về cách sử dụng ở trên. Tôi không sử dụng hộp chứa DI. Với thông tin tôi đã cung cấp ở trên, có vẻ như những lỗi này xảy ra do đồng thời: trong thời gian giữa 'OnAuthorization' và' AuthorizeCore', một yêu cầu khác sẽ kích hoạt 'OnAuthorization' và ghi đè thuộc tính' DbContext'. Điều này có làm theo không? –

+0

Vâng, đó là vấn đề của bạn. [Ủy quyền] về cơ bản là một singleton và bạn đang thay đổi thuộc tính dbcontext với mọi yêu cầu. Tôi đề nghị sử dụng DI Container, đăng ký DbContext với tuổi thọ HttpPerInstance rồi sử dụng DependencyResolver.Current.GetService () trong phương thức OnAuthorization. Các container nên xử lý việc xử lý DbContext cũng như – MikeSW

Trả lời

0

Trước tiên, tôi khuyên bạn nên "thực sự" quen thuộc với ASP.NET Application Life Cycle Overview for IIS 7.0 vì đó là cơ bản để thiết kế ứng dụng MVC tốt.

Bây giờ để thử và cơ sở mã của bạn "bắt chước"

Hãy nói rằng bạn có một MembershipProvider tùy chỉnh tương tự như mô tả ở đây https://stackoverflow.com/a/10067020/1241400

sau đó bạn sẽ chỉ cần một tùy chỉnh Authorize thuộc tính

public sealed class AuthorizeByRoles : AuthorizeAttribute 
    { 
     public AuthorizeByRoles(params UserRoles[] userRoles) 
     { 
      this.Roles = AuthorizationHelper.GetRolesForEnums(userRoles); 
     } 
    } 

public static class AuthorizationHelper 
{  
    public static string GetRolesForEnums(params UserRoles[] userRoles) 
    { 
     List<string> roles = new List<string>(); 
     foreach (UserRoles userRole in userRoles) 
     { 
      roles.Add(GetEnumName(userRole)); 
     } 
     return string.Join(",", roles); 
    } 

    private static string GetEnumName(UserRoles userRole) 
    { 
     return Enum.GetName(userRole.GetType(), userRole); 
    }   
} 

mà bạn có thể sử dụng trên bất kỳ bộ điều khiển hoặc hành động cụ thể nào

[AuthorizeByRoles(UserRoles.Admin, UserRoles.Developer)] 
public class MySecureController : Controller 
{ 
     //your code here 
} 

Nếu bạn muốn, bạn cũng có thể đăng ký sự kiện PostAuthorizeRequest và hủy kết quả dựa trên một số tiêu chí.

protected void Application_PostAuthorizeRequest(Object sender, EventArgs e) 
     { 

      //do what you need here 
     } 

Đối với DbContext, tôi chưa bao giờ gặp phải tình huống của bạn và có per request là cách tiếp cận đúng, do đó bạn có thể vứt bỏ nó trong bộ điều khiển hoặc trong kho lưu trữ của bạn.

Tất nhiên, bạn nên sử dụng filters và sau đó thêm thuộc tính [AllowAnonymous] vào hành động của mình.

1

Bạn có thực sự cần phải vứt bỏ ngữ cảnh không?

Theo this post bởi Jon Gallant người đã liên lạc với đội Microsoft ADO.NET Entity Framework:

Tôi luôn luôn phải gọi Dispose() trên đối tượng DbContext của tôi? Nope

Trước khi tôi nói chuyện với các nhà phát triển trong đội ngũ EF, câu trả lời của tôi luôn là một "tất nhiên" vang dội. Nhưng điều đó không đúng với DbContext. Bạn không cần phải tôn giáo về việc gọi Dispose trên các đối tượng DbContext của bạn. Mặc dù nó thực hiện IDisposable, nó chỉ thực hiện nó để bạn có thể gọi Dispose như một biện pháp bảo vệ trong một số trường hợp đặc biệt. Theo mặc định, DbContext sẽ tự động quản lý kết nối cho bạn.

Các vấn đề liên quan