Reputation: 1249
i am using asp.net mvc 4 and entity framework 5 in a project. i have a base Entity that all entities derived from it:
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; }
}
First the Account Entity is a class for application user:
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; }
}
Role of the User :
public class Role : BaseEntity
{
public string RoleName { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
each User can have multiple Role and vice versa:
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; }
}
when i want to give roles for an specific user, call GetRoles method in Accountrepository. this is implemented in this way:
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
}
in this method, an exception has thrown when the compiler want to run above LINQ query. this exception is:
StackOverflowException was unhandled
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
{Cannot evaluate expression because the current thread is in a stack overflow state.}
the GetRoles method are call two time :
one time from the Custom Authorize Attribute:
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;
}
}
and second time from the Application_AuthenticateRequest method in the Global.asax.cs :
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 that ou can see it in above method is implemented in this way:
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;
}
}
According to your idea, what is the problem? regards
Upvotes: 0
Views: 8123
Reputation: 14677
You have done few things which can lead you to troubles. The one I can see is the circular reference of Accounts, roles in AccountinRoles and vice versa.
I have simplified your code though it's not the best design(But I believe in keeping things simple and stupid). You can keep your virtual properties if you really mean what the virtual properties are for in entities.
This working and running fine.
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();
}
}
Upvotes: 1