Getting Started with Trackable Entities using Repository and Unit of Work Patterns

NOTE: While it is possible to create a Visual Studio 2012 solution with Repository and Unit of Work patterns using the Trackable.Patterns NuGet package, the item and project templates shown in this tutorial are only available for Visual Studio 2013 at this time.

Compatibility

  • Visual Studio 2013 with Web API 2 and EF 6.0 or later

Prerequisites

Make sure you have installed the Trackable Entities for Visual Studio 2013 Extension .

  • The extension can be installed from within Visual Studio by selecting the Tools menu, Extensions and Updates, Online tab.

Be sure to download the NorthwindSlim database and attach it to SQL Express using SQL Server Management Studio.

  • This is a stripped down version of the Northwind sample database.
  • Download the NorthwindSlim database from here: http://bit.ly/northwindslim

Details instructions for installing these prerequisites can be found here.

Trackable Web API with Repository and Unit of Work

  1. Start by selecting New Project from the File menu.  Under Visual C#, select the Trackable category, then choose “Trackable Web API with Repository and Unit of Work”.
    • Type an appropriate Name and click OK.

    Repo-UoW-NewProject

    - What you get is a solution containing 4 projects:
       ConsoleClient
       Client.Entities
       WebApi
       Service.Entities
       Service.Persistence
       Service.EF

    Repo-UoW-SolExpl

    • The solution also contains a ReadMe file with step-by-step instructions similar to this tutorial.

  2. The first thing you’ll want to do is create some server-side entities and a DbContext for persistence to a database. While you could hand-craft these classes yourself, it’s sometimes easier to start with an existing database and use the Entity Framework Power Tools to reverse engineer Code First classes. That’s what we’ll do here.
    - The Service.Entities project includes custom T4 templates which generate entity classes which implement the ITrackable interface.
    • Right-click the Service.Entities project in the solution explorer and select Entity Framework from the context menu.
    • Then select Reverse Engineer Code First.

    Reverse-Eng

    • Fill out the Connection Properties dialog and click OK.

    cn-prop7_thumb

    • What you get is a set of entities, mapping classes, and a DbContext-derived class.
    • Here you can see classes generated for the NorthwindSlim database.

    Reverse-Eng-Classes

    • Build the solution.
  3. Copy the connection string from the App.config file in the Service.Entities project to the Web.config file in the WebApi project.
    - For example:
    <connectionStrings>
      <add name="NorthwindSlimContext" connectionString="Data Source=.\sqlexpress;Initial Catalog=NorthwindSlim;Integrated Security=True;MultipleActiveResultSets=True"
        providerName="System.Data.SqlClient" />
    </connectionStrings>
  4. Add Repository Interfaces to the Service.Persistence project.
    - Right-click the Repositories folder, Add New Item, Trackable category, select "Entity Repository Interface".

    • Enter an interface name, for example: ICustomerRepository

    Entity-Repo-Interface

    • Select a trackable entity from the drop down in wizard, for example: Customer.
    • Also enter an entity set name, for example: Customers 

      Entity-Repo-ICustomer

    • What you get is an entity repository interface for Customer with methods for getting all customers, getting a customer by id, and deleting a customer.
      - Feel free to add or remove interface methods according to your own needs.
    • NOTE:  If needed change the parameter type for the Get and Delete methods to match the primary key property of the entity.
      For example: Task<Customer> GetCustomer(string id);
    public interface ICustomerRepository : IRepository<Customer>, IRepositoryAsync<Customer>
    {
        Task<IEnumerable<Customer>> GetCustomers();
        Task<Customer> GetCustomer(string id);
        Task<bool> DeleteCustomer(string id);
    }
  5. Add other Entity Repository Interfaces as needed.
    - For example: Order
    • Add other methods to query entities as needed, for example:
      Task<IEnumerable<Order>> GetOrders(string customerId); 
    public interface IOrderRepository : IRepository<Order>, IRepositoryAsync<Order>
    {
        Task<IEnumerable<Order>> GetOrders();
        Task<IEnumerable<Order>> GetOrders(string customerId);
        Task<Order> GetOrder(int id);
        Task<bool> DeleteOrder(int id);
    }
  6. Add a Unit of Work Interface to the Service.Persistence project.
    - Right-click the UnitsOfWork folder, Add New Item, Trackable category, select "Example Unit of Work Interface".
    - Enter a name, for example: INorthwindUnitOfWork

    UoW-Interface

    • Add read-only properties for each entity repository interface, for example:
      - NOTE: You will need to resolve namespaces for the Entity Repository Interfaces by adding the required using directives.
      public interface INorthwindUnitOfWork : IUnitOfWork, IUnitOfWorkAsync
      {
          ICustomerRepository CustomerRepository { get; }
          IOrderRepository OrderRepository { get; }
      }
    • After adding repository and unit of work interfaces to the Service.Persistence project, Repositories and UnitsOfWork folders should contain interface files.

      Service-Persist-Proj

  7. Add a Database Context Interface to the Service.EF project.
    - Right-click the Contexts folder, Add New Item, Trackable category, select "Database Context Interface".       
    • Enter an interface name, for example: INorthwindSlimContext

      DbContext-Interface

    • Add IDbSet<Entity> properties for each entity set on the DbContext class.
      - NOTE: You will need to resolve namespaces for the entity classes by adding the required using directive.
    public interface INorthwindSlimContext
    {
        IDbSet<Category> Categories { get; set; }
        IDbSet<Customer> Customers { get; set; }
        IDbSet<Order> Orders { get; set; }
        IDbSet<OrderDetail> OrderDetails { get; set; }
        IDbSet<Product> Products { get; set; }
    }
  8. Now copy the DbContext generated by the EF Power Tools from the Service.Entities project
    over to the Contexts folder in the Service.EF project. For example: NorthwindSlimContext
    • Remember to delete the DbContext class from the Service.Entities project

      EF-Context

    • Change the namespace in which the DbContext class appears to match that of the Service.EF project
      - NOTE: You will need to resolve namespaces for the entity classes by adding the required using directive.
    • Next, modify the DbContext class to implement the Database Context Interface you just added, for example:
      public partial class NorthwindSlimContext : DbContext, INorthwindSlimContext
    • Then change each DbSet property to IDbSet, so that interface is implemented
      namespace TrackableGettingStarted.Service.EF.Contexts
      {
          public partial class NorthwindSlimContext : DbContext, INorthwindSlimContext
          {
              public IDbSet<Category> Categories { get; set; }
              public IDbSet<Customer> Customers { get; set; }
              public IDbSet<Order> Orders { get; set; }
              public IDbSet<OrderDetail> OrderDetails { get; set; }
              public IDbSet<Product> Products { get; set; }
    • Build the solution to make sure everything compiles
  9. Add Entity Repository Classes to the Service.EF project.
    - Right-click the Repositories folder, Add New Item, Trackable category, select "Entity Repository Class".
    • Enter an class name, for example: CustomerRepository

      Customer-Repo-Class

    • Select a trackable entity from the drop down list in the wizard
      and enter an entity set name, for example: Customer, Customers

      Customer-Entity-Repo

    • Rename IDatabaseContext to match the name of the Database Context Interface you added earlier,
      for example: INorthwindSlimContext
      -
      If needed change the parameter type for the Get and Delete methods to match the primary key property of the entity.
      For example, change int to string for GetCustomer and DeleteCustomer.
      public class CustomerRepository : Repository<Customer>, ICustomerRepository
      {
          private readonly INorthwindSlimContext _context;
      
          public CustomerRepository(INorthwindSlimContext context) :
              base(context as DbContext)
          {
              _context = context;
          }
    • Add other Entity Repository Classes as needed, for example, OrderRepository, replacing the Data Context Interface as before.
      - Implement other methods defined on the IOrderRepository interface: GetOrders(string customerId).
      - Add Include statements as needed to the Get methods for including child entities.
      public async Task<IEnumerable<Order>> GetOrders()
      {
          IEnumerable<Order> orders = await _context.Orders
              .Include(o => o.Customer)
              .Include("OrderDetails.Product")
              .ToListAsync();
          return orders;
      }
      
      public async Task<IEnumerable<Order>> GetOrders(string customerId)
      {
          IEnumerable<Order> orders = await _context.Orders
              .Include(o => o.Customer)
              .Include("OrderDetails.Product")
              .Where(o => o.CustomerId == customerId)
              .ToListAsync();
          return orders;
      }
      
      public async Task<Order> GetOrder(int id)
      {
          Order order = await _context.Orders
                  .Include(o => o.Customer)
                  .Include("OrderDetails.Product")
                  .SingleOrDefaultAsync(o => o.OrderId == id);
          return order;
      }
    • Build the solution to make sure everything compiles.
  10. Add a Unit of Work Class to the Service.EF project.
    - Right-click the UnitsOfWork folder, Add New Item, Trackable category, select "Example Unit of Work Class".
    • Enter a class name that matches the Unit of Work Interface added earlier,
      to the Service.Persistence project, for example: NorthwindUnitOfWork

      UoW-Class

    • Add read-only fields for each entity repository interface, for example:
      private readonly ICustomerRepository _customerRepository
    • Modify the class ctor by renaming IDatabaseContext to match the context
      interface you added earlier, for example: INorthwindSlimContext
    • Then add repository interface parameters for each field you added,
      and Initialize each entity repository field using the ctor parameters.
      public class NorthwindUnitOfWork : UnitOfWork, INorthwindUnitOfWork
      {
          private readonly ICustomerRepository _customerRepository;
          private readonly IOrderRepository _orderRepository;
      
          public NorthwindUnitOfWork(INorthwindSlimContext context,
              ICustomerRepository customerRepository,
              IOrderRepository orderRepository) :
              base(context as DbContext)
          {
              _customerRepository = customerRepository;
              _orderRepository = orderRepository;
          }
    • Next add read-only property for each entity repository interface.
      public ICustomerRepository CustomerRepository
      {
          get { return _customerRepository; }
      }
      
      public IOrderRepository OrderRepository
      {
          get { return _orderRepository; }
      }
    • Lastly add a using directive to resolve the Exception classes namespace:
      using WebApiSample.Service.Persistence.Exceptions;
    • Also resolve any another namespaces as needed:
      Contexts, Exceptions, Repositories, UnitsOfWork
    • Build the solution to make sure everything compiles.
  11. Add Entity Web API Controllers to the WebApi project.
    - Right-click the Controllers folder, Add New Item, Trackable category, select "Entity Web API Controller".

      WebApi-Controllers

    • Enter a name that matches an entity, for example: CustomerController

      Customer-Controller

      Customer-Controller-Wizard

    • Rename IExampleUnitOfWork to match Unit of Work Interface added to Persistence project,
      for example: INorthwindUnitOfWork
      - NOTE: If needed change the parameter type for the Get and Delete methods
      to match the primary key property of the entity, for example: GetCustomer(string id)
      public class CustomerController : ApiController
      {
          private readonly INorthwindUnitOfWork _unitOfWork;
      
          public CustomerController(INorthwindUnitOfWork unitOfWork)
          {
              _unitOfWork = unitOfWork;
          }
    • Add other controllers as needed, for example: OrderController.
      - Add other Get methods supported by the Entity Repository Interface, for example: GetOrders(string customerId)
      // GET api/Order?customerId=ABCD
      [ResponseType(typeof(IEnumerable<Order>))]
      public async Task<IHttpActionResult> GetOrders(string customerId)
      {
          IEnumerable<Order> orders = await _unitOfWork.OrderRepository.GetOrders(customerId);
          return Ok(orders);
      }
    • Build the solution to make sure everything compiles.
  12. Configure the IoC container to register required types on startup of the WebApi app.
    - Open IoCConfig.cs from with the App_Start folder.
    - Uncomment the using directives: EF.Contexts, EF.Repositories, EF.UnitsOfWork,
       Persistence.Repositories, Persistence.UnitsOfWork
    .
    - Register Context, UnitofWork and Repositories with the container.
    • Use PerRequestSingleton for the lifetime model.
      - For this example, simply uncomment the four lines of code in IoCConfig.
      public static class IoCConfig
      {
          public static void Register(HttpConfiguration config)
          {
              // Get IoC container
              var container = TinyIoCContainer.Current;
      
              // Register context, unit of work and repos with per request lifetime
              container.Register<INorthwindSlimContext, NorthwindSlimContext>().AsPerRequestSingleton();
              container.Register<INorthwindUnitOfWork, NorthwindUnitOfWork>().AsPerRequestSingleton();
              container.Register<ICustomerRepository, CustomerRepository>().AsPerRequestSingleton();
              container.Register<IOrderRepository, OrderRepository>().AsPerRequestSingleton();
      
              // Set Web API dep resolver
              config.DependencyResolver = new TinyIoCDependencyResolver(container);
          }
      }
  13. Run the WebApi project to test the controller.
    - Righ-click on the WebApi project and select View in Browser. 
    • Click the API link on the home page.
    • Select an operation, such as GetCustomers or GetOrder.
    • Click the Test API button, fill out the form and click Send.
      - You should see JSON for the response body
  14. Reverse engineer trackable entities for Client.Entities project.
    - Right-click, Entity Framework, Reverse Engineer Code First.
    • Enter the connection information: server, database name.
      - For this sample we are using the NorthwindSlim database.
    • Delete the Mapping folder.
    • Delete the data context class: NorthwindSlimContext.
      - Then build the solution.
  15. Lastly, flesh out code in the ConsoleClient project to invoke controller actions for the Web API service.
    - Uncomment code in Program.Main.
    • Replace the port number for HttpClient with that shown in the Web API home page.
    • Set the ConsoleClient project as the startup project for the solution, then press F5 or Ctrl+F5 to run the console client app.
    • Press Enter to start the client.
      - You should see all the customers in the database printed to the console.
      - Enter a customer id to retrieve orders for that customer, for example: ALFKI.
      - You should see a list of orders for the selected customer.
      - Enter one of the order id's shown to retrieve that order with details.
    • Then press Enter to create a new order for the selected customer.
      - You should see a new order with three new order details.
    • Press Enter to update the newly created order.
      - You should see that the the first order detail's price has increased,
         the second order detail was deleted, the third order detail was left unchanged,
         and a new order detail was added.
    • Press Enter to delete the updated order.
      - NOTE: To debug the ConsoleClient and WebApi project at the same time, simply
         set the startup projects for the solution to start both. Then set a breakpoint
         in the desired controller action
      .

So that is how you can get started using the Trackable Entities for Visual Studio 2013 to create a Web API service that uses the Repository and Unit of Work patterns.

Last edited Apr 28, 2014 at 2:07 PM by tonysneed, version 3