3
\$\begingroup\$

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:

Flow Diagram

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");
    }
}
\$\endgroup\$
  • \$\begingroup\$ GetRepository<T>() returns dynamic ... is there any way I could bypass that? \$\endgroup\$ – Gaui Aug 28 '14 at 23:36
2
\$\begingroup\$

For the GetRepository question, if you're using an ORM, I tend to add an interface to my modeling classes that I can call generically on an abstract repository class.

Something like

public abstract class GenericRepository<T> : IGenericRepository<T> where T : IModel 
{
    //Implementation
}

You can return the interface instead on your GetRepository method

public IGenericRepository<T> GetRepository<T>() where T : IModel
{
    //WILL NEED TO RE-IMPLEMENT.
}

This would probably require some significant restructuring of your base repo classes though.

\$\endgroup\$
  • \$\begingroup\$ Actually, just changed dynamic return type to T :) \$\endgroup\$ – Gaui Aug 29 '14 at 22:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.