0

我在一个项目中使用 asp.net mvc 4 和 entity framework 5。我有一个基础实体,所有实体都从它派生:

public abstract class BaseEntity
{        
    [Required]
    public virtual int Id { get; set; }

    [Required]
    public virtual DateTime CreatedOn { set; get; }

    public virtual string CreatedBy { set; get; }

    [Required]
    public virtual DateTime ModifiedOn { set; get; }

    public virtual string ModifiedBy { set; get; }
}

首先,Account Entity 是应用程序用户的一个类:

public class Account : BaseEntity
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public byte[] AvatarBinary { get; set; }
    public string AvatarMimeType { get; set; }

    public virtual IList<AccountInRole> AccountRoles { get; set; }
}

用户角色:

public class Role : BaseEntity
{
    public string RoleName { get; set; }

    public virtual IList<AccountInRole> AccountRoles { get; set; }
}

每个用户可以有多个角色,反之亦然:

public class AccountInRole : BaseEntity
{
    public int AccountId { get; set; }
    public int RoleId { get; set; }

    public virtual Account Account { get; set; }
    public virtual Role Role { get; set; }
}

当我想为特定用户赋予角色时,请在 Accountrepository 中调用GetRoles方法。这是以这种方式实现的:

public class AccountRepository : IAccountRepository
{
#region Properties 
private CharityContext DataContext { get; set; }

public IQueryable<Account> Accounts
{
    get { return DataContext.Accounts; }
}

#endregion

#region Ctors 

public AccountRepository() : this(new CharityContext())
{
}

public AccountRepository(CharityContext db)
{
    DataContext = db;
}

#endregion

#region Methods

public List<Role> GetRoles(string userName)
{
    var acc = DataContext.Accounts;

    var query = from u in DataContext.Accounts
                from r in DataContext.Roles
                from ur in DataContext.AccountInRoles
                where ur.AccountId == u.Id && ur.RoleId == r.Id && u.UserName == userName
                select r;
    return query.ToList();
}

#endregion

}

在此方法中,当编译器要在上面运行 LINQ 查询时引发了异常。这个例外是:

StackOverflowException 未处理

mscorlib.dll 中出现“System.StackOverflowException”类型的未处理异常

{无法计算表达式,因为当前线程处于堆栈溢出状态。}

GetRoles 方法被调用两次:

一次来自自定义授权属性:

public class CustomAuthorize  : AuthorizeAttribute
{   
    //private readonly IAccountRepository _accountRepository;
    private string[] roles;

    //public CustomAuthorize(params string[] roles)
    //{
    //    this.roles = roles;
    //}

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext == null)
            throw new ArgumentNullException("httpContext");

        if (!httpContext.User.Identity.IsAuthenticated)
            return false;

        if (Roles == string.Empty)
            return true;

        var lstRoles = Roles.Split(',');
        AccountRepository _accountRepository = new AccountRepository();
        var userRoles = _accountRepository.GetRoles(httpContext.User.Identity.Name);
        foreach (var role in lstRoles)
        {
            bool isFound = false;
            foreach (var userRole in userRoles)
            {
                if (userRole.RoleName == role)
                    isFound = true;
            }
            if (!isFound) return false;
        }

        return true;
    }        
}

第二次来自 Global.asax.cs 中的 Application_AuthenticateRequest 方法:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    string cookie = FormsAuthentication.FormsCookieName;
    HttpCookie httpCookie = Request.Cookies[cookie];
    if (httpCookie == null) return;
    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(httpCookie.Value);
    if(ticket == null || ticket.Expired) return;
    FormsIdentity identity = new FormsIdentity(ticket);
    var _accountRepository = new AccountRepository();
    var roles = _accountRepository.GetRoles(identity.Name);
    var principal = new CharityAccount(identity.Name, roles.Select(x => x.RoleName).ToArray());
    Context.User = Thread.CurrentPrincipal = principal;
}

您可以在上面的方法中看到它的 CharityAccount 是这样实现的:

public class CharityAccount : IPrincipal 
{
    private string[] roles;
    private IIdentity identity;

    public IIdentity Identity
    {
        get { return identity; }
    }

    public bool IsInRole(string role)
    {
        return Array.IndexOf(roles, role) >= 0;
    }

    public CharityAccount(String name, String[] roles)
    {
        identity = new GenericIdentity(name, "Custom authentication");
        this.roles = roles;
    }
}

按照你的想法,问题出在哪里?问候

4

1 回答 1

1

你做了很少的事情会导致你陷入困境。我可以看到的是 Accounts 的循环引用,AccountinRoles 中的角色,反之亦然。

我已经简化了你的代码,虽然它不是最好的设计(但我相信保持简单和愚蠢)。如果您真的是指虚拟属性在实体中的用途,则可以保留您的虚拟属性。

这工作和运行良好。

  public abstract class BaseEntity
{
    public int Id { get; set; }
    public DateTime CreatedOn { set; get; }
}

public class Account : BaseEntity
{
    public string UserName { get; set; }
    public string Password { get; set; }
}

public class Role : BaseEntity
{
    public string RoleName { get; set; }
}

public class AccountInRole
{
    public int AccountId { get; set; }
    public int RoleId { get; set; }
}

public class Operation
{
    public List<Role> GetRoles()
    {
        List<Account> lstAccount = new List<Account>();
        List<Role> lstRole = new List<Role>();
        List<AccountInRole> lstAccountInRoles = new List<AccountInRole>();

        Account ac1 = new Account
            {
                Id = 1,
                UserName = "Jack",
                Password = "somePassword2",
                CreatedOn = DateTime.Now
            };
        Account ac2 = new Account
        {
            Id = 2,
            UserName = "Sam",
            Password = "somePassword1",
            CreatedOn = DateTime.Now
        };
        lstAccount.Add(ac1);
        lstAccount.Add(ac2);

        Role r1 = new Role
            {
                Id = 1,
                RoleName = "TestRole1",
                CreatedOn = DateTime.Now
            };
        Role r2 = new Role
        {
            Id = 2,
            RoleName = "TestRole2",
            CreatedOn = DateTime.Now
        };
        lstRole.Add(r1);
        lstRole.Add(r2);

        AccountInRole acRole1 = new AccountInRole
            {
                AccountId = ac1.Id,
                RoleId = r1.Id
            };

        AccountInRole acRole2 = new AccountInRole
        {
            AccountId = ac2.Id,
            RoleId = r2.Id
        };
        lstAccountInRoles.Add(acRole1);
        lstAccountInRoles.Add(acRole2);

        string userName = "Sam";

        // Query the data
        var roles = from u in lstAccount
                    where u.UserName == userName
                    from acc in lstAccountInRoles
                    from r in lstRole
                    where acc.AccountId == u.Id
                    && r.Id == acc.RoleId
                    select r;

        return roles.ToList();
    }
}
于 2013-06-24T20:18:11.533 回答