I'm implementing a Web API that communicates with old WCF services and want to make it future proof.
The Web API consists of:
- API controllers
- Service Providers
- Repositories
I've implemented a service and a repository layer. A repository factory is injected into each service, because services can communicate with more than one repository.
The API controllers use service providers, which use a repository which can be tied to a WCF service, database or whatever. For now it's only tied to a WCF service for data, but because these WCF services are old I would like to be able to replace them later on with minimal effort or go straight to the database (using ORM).
Current WCF services deliver polluted complicated objects, so I'm also using the repository layer to filter these objects to a well structured POCOs.
I want to be able to:
- Easily replace a data store (WCF service, database or whatever)
- Easily test business logic in isolation (with mock data)
A simple flow diagram would be:
Edit 29/08/14: Managed to eliminate the dynamic return type. Changed the return type to T
and casted the returned repository to type T
:
public T GetRepository<T>()
{
// If the repository exists, return it
if (this.repositories.ContainsKey(typeof(T)))
{
return (T)this.repositories[typeof(T)];
}
// Repository doesn't exist, throw an exception
throw new RepositoryNotFoundException("No repository found for " + typeof(T).Name);
}
AuthenticationController
public class AuthenticationController : ApiController
{
AuthServiceProvider authProvider;
public AuthenticationController()
{
RepositoryFactory repoFactory = new RepositoryFactory();
repoFactory.AddRepository<IAuthServiceRepository>(new AuthServiceRepository());
repoFactory.AddRepository<ISystemServiceRepository>(new SystemServiceRepository());
authProvider = new AuthServiceProvider(repoFactory);
}
[Route("login")]
[HttpPost]
public Authenticate Login(Login login)
{
var auth = authProvider.Login("foobar7", login.Username, login.Password);
return auth;
}
[Route("profile")]
[HttpGet]
public UserProfile Profile()
{
return authProvider.GetUserProfile("1f5f36b3-e3d2-4fa2-8b0a-90176a90eedb");
}
}
AuthServiceProvider
public class AuthServiceProvider
{
RepositoryFactory repoFactory;
public AuthServiceProvider(RepositoryFactory repo)
{
repoFactory = repo;
}
public Authenticate Login(string sessionID, string username, string password)
{
var auth = repoFactory.GetRepository<IAuthServiceRepository>().AuthenticateWebUser(sessionID, username, password);
// Business logic here
return auth;
}
public UserProfile GetUserProfile(string accessToken)
{
var profile = repoFactory.GetRepository<IAuthServiceRepository>().GetUserProfileByKey(accessToken);
// Business logic here
return profile;
}
}
RepositoryFactory
public class RepositoryFactory
{
/// <summary>
/// Dictionary with repositories
/// </summary>
private Dictionary<Type, IServiceRepository> repositories;
/// <summary>
/// Default constructor
/// </summary>
public RepositoryFactory()
{
// Create a new dictionary that stores Type and some repository that implements IServiceRepository
this.repositories = new Dictionary<Type, IServiceRepository>();
}
/// <summary>
/// Adds a repository
/// A repository is identified by its interface
/// </summary>
/// <typeparam name="T">Type of repository (e.g. ISomeServiceRepository)</typeparam>
/// <param name="repo">The repository itself (class that implements the type)</param>
public void AddRepository<T>(IServiceRepository repo) where T : class
{
// If the repository exists, remove it
if (this.repositories.ContainsKey(typeof(T)))
{
this.repositories.Remove(typeof(T));
}
// Add the repository
this.repositories.Add(typeof(T), repo);
}
/// <summary>
/// Dynamically get a repository
/// </summary>
/// <typeparam name="T">Type of repository (e.g. ISomeServiceRepository)</typeparam>
/// <returns>The repository</returns>
public T GetRepository<T>()
{
// If the repository exists, return it
if (this.repositories.ContainsKey(typeof(T)))
{
return (T)this.repositories[typeof(T)];
}
// Repository doesn't exist, throw an exception
throw new RepositoryNotFoundException("No repository found for " + typeof(T).Name);
}
}
IServiceRepository
public interface IServiceRepository
{
}
IAuthServiceRepository
public interface IAuthServiceRepository : IServiceRepository
{
Authenticate AuthenticateWebUser(string sessionID, string username, string password);
UserProfile GetUserProfileByKey(string key);
}
AuthServiceRepository
public class AuthServiceRepository : IAuthServiceRepository
{
public Authenticate AuthenticateWebUser(string sessionID, string username, string password)
{
using (UserAuthenticateProxy.UserServiceClient proxy = new UserAuthenticateProxy.UserServiceClient())
{
var auth = proxy.AuthenticateWebUser(sessionID, username, password);
return new Authenticate
{
UserID = auth.Id,
IsEmployee = auth.IsEmployee,
Key = auth.Key,
LoginSystem = 1000
}
}
}
public UserProfile GetUserProfileByKey(string key)
{
using (UserAuthenticateProxy.UserServiceClient proxy = new UserAuthenticateProxy.UserServiceClient())
{
var profile = proxy.GetUserProfileByKey(key);
return new UserProfile
{
Info = profile.UserInfo,
IsEmployee = profile.Employee,
Roles = profile.Roles,
Companies = profile.Companies,
Menu = profile.Menu,
AccessToken = profile.AccessToken,
SessionID = profile.SessionID
}
}
}
}
Here is a sample unit test:
[TestClass]
public class AuthServiceProviderTest
{
[TestMethod]
public void TestLoginForEmployee()
{
/*
ARRANGE
*/
// Create a new repository factory
RepositoryFactory repoFactory = new RepositoryFactory();
// Create a mock of AuthServiceRepository
var authServiceRepoMock = new Mock<IAuthServiceRepository>();
authServiceRepoMock.Setup(x => x.AuthenticateWebUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new Authenticate
{
UserID = 13,
IsEmployee = true,
Key = "SOME-KEY",
LoginSystem = 1000
});
// Add AuthServiceRepository to repository factory
repoFactory.AddRepository<IAuthServiceRepository>(authServiceRepoMock.Object);
// Create a AuthServiceProvider with the repositories
AuthServiceProvider authProvider = new AuthServiceProvider(repoFactory);
/*
ACT
*/
var login = authProvider.Login("session", "user", "pass");
/*
ASSERT
*/
Assert.AreEqual(login.UserID, 13, "User ID is not correct");
Assert.IsTrue(login.IsEmployee, "This should be an employee");
Assert.AreEqual(login.Key, "SOME-KEY", "This should be an employee");
Assert.AreEqual(login.LoginSystem, 1000, "Login system is not correct");
}
}
GetRepository<T>()
returns dynamic ... is there any way I could bypass that? \$\endgroup\$ – Gaui Aug 28 '14 at 23:36