StackOverflowException未处理:类型为' System.StackOverflowException'的未处理异常发生在mscorlib.dll中

时间:2013-06-24 19:15:59

标签: asp.net-mvc entity-framework exception asp.net-mvc-4 entity-framework-5

我在项目中使用asp.net mvc 4和实体框架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; }
}

首先,帐户实体是应用程序用户的类:

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

根据你的想法,问题是什么? 问候

1 个答案:

答案 0 :(得分:1)

你做了一些可能导致麻烦的事情。我能看到的是账户的循环引用,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();
    }
}