Reputation: 13367
Recently I adopted the Repository/Service design pattern to handle objects in my MVC 4 project.
It was definitely a good move, but I have found an issue :) Now this could be down to my application of the pattern, so I thought I would put it out there and see if anyone can suggest a solution or a design pattern to fit my needs.
I have a Repository and a Service to handle custom users. This works perfectly for Getting users and performing any user method. But, before I moved to the Repository/Service design pattern I had a static class that handled all of the user related methods.
One of these methods was:
public static Profile CurrentUser()
{
// TODO: Need to replace the Profile session when any permissions are changed....
var companyId = HttpContext.Current.Session["CompanyId"].ToString();
if (HttpContext.Current.User.Identity.IsAuthenticated && !string.IsNullOrEmpty(companyId))
{
var userId = Membership.GetUser(HttpContext.Current.User.Identity.Name).ProviderUserKey.ToString();
var repository = new ProfileRepository(companyId);
return repository.Get(userId);
}
else
return null;
}
As you can see, this method returns the Profile of a particular user if they are logged in. If they are not, then it returns null.
My issue is that that method needs to be seperate from the Service and Repository because it has to be called from any controller which is why I made it static.
To solve this issue I created a class called ProfileContext and CurrentUser() is the only method in this static class.
So my question is simple. Would this be the best way to handle the CurrentUser method or is there another, better solution?
Cheers, /r3plica
Upvotes: 1
Views: 6574
Reputation: 693
I had a similar problem and to solve it, I made some services singletons. Then I made a Configure method that can be called only once. Then you can call that method in your global.asax file
SecurityService.cs:
public sealed class SecurityService {
private ISecurityRepository _repository;
public static SecurityService Instance { get; private set; }
private SecurityService(ISecurityRepository repository) {
_repository = repository;
}
public static void Configure(ISecurityRepository repository) {
Instance = new SecurityService(repository);
}
public Model.LoginResponse GetUser(Model.UserRequest request) {
if (string.IsNullOrEmpty(request.SecurityToken)) return null;
User user = _repository.GetUserByToken(request.SecurityToken);
if (user == null) throw new HabitationException("Invalid Security Token. Please login.", null);
return AutoMapper.Mapper.Map<Model.LoginResponse>(user);
}
public Model.LoginResponse Login(Model.LoginRequest request) {
string strPassword = EncryptPassword.GetMd5Hash(request.Password);
User user = _repository.GetUser(request.Username, strPassword);
return AutoMapper.Mapper.Map<Model.LoginResponse>(user);
}
internal User GetLoggedInUser(string securityToken) {
if (string.IsNullOrEmpty(securityToken)) {
return null;
}
User user = _repository.GetUserByToken(securityToken);
if (user == null) throw new HabitationException("Invalid Security Token.", null);
return user;
}
}
global.asax:
protected void Application_Start() {
MyProject.LinqToSQL.Repositories.SecurityRepository securityRepository = new MyProject.Repository.LinqToSQL.Repositories.SecurityRepository(connection);
MyProject.Repository.HttpCache.Repositories.SecurityRepository securityCacheRepository = new MyProject.Repository.HttpCache.Repositories.SecurityRepository(securityRepository);
MyProject.Domain.Service.Interface.SecurityService.Configure(securityCacheRepository);
}
note: there are two repositories here because I am using the Strategy Pattern. Some of my repositories can be passed additional repositories and if they don't find the object, they can call the nested repository. In this case, the effect is that if it's not in the HttpCache, it will check the database. But that's a bit off topic.
Now, in another service you can call any of the instance methods:
SecurityService.Instance.GetLoggedInUser(SecurityToken);
note: in my example I'm not using Dependency Injection for the service classes, only the repository ones.
Upvotes: 0
Reputation: 1316
If I understand correctly, your current solution is to use a static class (ProfileContext
) which has the method outlined in your question.
This isn't too bad but generally static classes are best avoided because they really don't play nice with unit testing.
The general pattern to deal with static classes is a wrapper.
e.g.
public interface IMembershipWrapper
{
MembershipUser GetUser(string name)
}
Your controllers would then have a dependency property:
public IMembershipWrapper membershipWrapper { get; set; }
For unit testing you can use a Mock. Your concrete implementation would look like this:
public class MembershipWrapper : IMembershipWrapper
{
public MembershipUser GetUser(string name)
{
return Membership.GetUser(string);
}
}
In your case, you could just create a wrapper for your ProfileContext
static class and use that.
Upvotes: 2