Repositories in the Partner Portal

This section describes the implementation of the Repository pattern in the Partner Portal application. It also includes an overview of the business entities used by the Partner Portal application.

A repository is a design pattern that separates a data source from its associated business logic by encapsulating the data access details. Repositories translate the underlying data representation into an entity model that fits the problem domain. The Partner Portal application uses the Repository pattern to encapsulate business entities used by the application. To do this, it makes use of the helper classes provided by the SharePoint Guidance Library.

For more information about the Repository pattern, see The Repository Pattern. For more information about the repository-related classes provided by the SharePoint Guidance Library, see List-Based Repositories.

Repositories in the Partner Portal Application

The following illustration shows the high-level design of the repositories that are implemented by the Partner Portal application:

High-level design of repositories

Ff647990.2328a3b5-91bc-44a1-97fe-d29df3815cf7(en-us,PandP.10).png

The client, such as a Web Part, requests a repository from the service locator. The client relies solely on the interface of the repository. This makes it possible to unit test the client in isolation. The test environment can provide a mock object that stands in for the repository.

The repository interface exposes business entities. Business entities are strongly typed classes that represent data records. Examples are the Product and Customer class. These classes do not contain any code about how the objects are stored and retrieved from the data store.

The repository's implementation interacts with the underlying data store. The repository can query a table in a database, update items in a SharePoint list or invoke Web services. The choice of data store determines the mode of data access. For example, to access items in a SharePoint list, you must interact with SPListItem objects. The repository maps these store-specific entities to and from the strongly typed business entities.

Repositories reduce the complexity of accessing the data store. For example, when calling Web services, a repository can perform the authentication instead of having the client code do it. Repositories can also implement caching to improve performance.

Repositories are hard to unit test because they are tightly integrated with the underlying data store. It is possible to use mock objects that simulate behavior of the underlying database or SharePoint list, but this requires a large investment in test code. For this reason, many developers choose to test repositories with integration tests.

The following illustration shows an example of a repository implementation from the Partner Portal application.

Design of the Partner Portal's pricing repository

Ff647990.3bbe6e83-8924-4e10-b823-488b4ad87da6(en-us,PandP.10).png

In this example, the interface and business entities of the repository are defined in the Contoso.Common assembly. The implementation is defined in the Contoso.PartnerPortal.LOB.Servcies.Client feature. When this feature is activated in SharePoint, the feature receiver calls the ServiceLocatorConfig class and maps the IPricingRepository interface to the PricingRepository class.

Splitting the repository's interface and the implementation of the repository into different assemblies is not required. However, if you do this, the client of the repository no longer requires a reference to the assembly that implements the repository. This is useful in the following situations:

  • You want to develop, version, and deploy the feature that holds the repositories separately from the client. For example, if the repository accesses a Web service, you do not need to recompile the client code if the Web service changes.
  • You want to be able to easily replace the implementation in the repositories. For example, in one environment you want to retrieve information from a database, but in another environment you want to retrieve the same information from a Web service.

Note

Separating the repository implementation from its interface adds complexity. There are many situations where this is not required.

The Partner Portal Data Model

The Contoso.Common namespace contains classes, interfaces, and enumerations that provide a conceptual data model that is used by the Partner Portal application. This section discusses the repository interfaces and the business entity classes.

The Contoso.Common.BusinessEntities namespace contains business entity classes that represent many of the data elements that are used by the Partner Portal application. Repository interface methods return instances of Contoso business entity classes. Repository interfaces are found in the Contoso.Common.Repositories namespace.

Partner Portal Business Entity Classes

Business entities are declared in the PartnerPortal\Contoso.Common\BusinessEntities directory. It contains the following classes:

  • Product class. Each Contoso product has a unique stock-keeping unit (SKU) in addition to other properties, such as a name and a description.
  • Category class. Products are organized by a hierarchy of categories. Each product has a category. Each category, except for a special root category, has a (more general) parent category.
  • Price class. Product pricing occurs on a per-partner basis. A product's price depends on the partner ID.
  • Discount class. Discounts are part of promotions that occur from time to time. Discounts are specific to a particular partner and product. There can be more than one discount for each partner and product pair.
  • Incident class. Incidents represent issues, such as the need for a replacement part, that require a high level of interaction between Contoso customer support and a partner. A SharePoint collaboration Web site is created for each active incident.
  • IncidentTask class. Incidents can contain tasks. An example of a task is to send a replacement part to a customer.
  • Part class. Some products are composed of parts.

Note

There is no business entity class for partners and for the product/part relationship. The current partner ID is deduced from the run-time security context of the current user. The product/part relationship is an internal detail of the product catalog.

Partner Portal Repository Interfaces

There are four repository interfaces defined by the Partner Portal application. They are defined in the PartnerPortal\Contoso.Common\Repositories directory. These interfaces are the following:

  • IProductCatalogRepository interface. The Partner Portal application uses the product catalog repository to look up a product using a product SKU and to enumerate products in a specified category. The application can enumerate subcategories of a category and parts that are used by a product.
  • IPricingRepository interface. The Partner Portal application uses the pricing repository to look up a price that applies to a product in the current partner context. The repository can also return an enumeration of discounts that apply to a product in considering the current partner context.
  • IIncidentManagementRepository interface. The Partner Portal application uses the incident management repository to look up an incident using an incident ID, write information to the incident history log and close an incident.
  • IIncidentTaskRepository interface. The Partner Portal application uses the incident task repository to retrieve a list of all open incident tasks, for all partners.

Repository Implementations

The Partner Portal application uses the SharePoint service locator to map repository interfaces to their corresponding implementations. This is shown in the following code.

IProductCatalogRepository productCatalogRepository =                     
         SharePointServiceLocator.Current.GetInstance<IProductCatalogRepository>();

The SharePoint service locator's mapping between interface and implementation is configured during the execution of a feature receiver that installs LOB services. This is shown in the following code, which is located in the PartnerPortal\Contoso.LOB.Services.Client\WebAppFeatureReceiver.cs file.

[CLSCompliant(false)]
[Guid("8b0f085e-72a0-4d9f-ac74-0038dc0f6dd5")]
public class UpdateServiceModelFeatureReceiver : SPFeatureReceiver
{
   // ...
        
   [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
   public override void FeatureInstalled(SPFeatureReceiverProperties properties)
   {
      ServiceLocatorConfig typeMappings = new ServiceLocatorConfig();

      // ...

      typeMappings.RegisterTypeMapping<IProductCatalogRepository, 
                                                CachedBdcProductCatalogRepository>();
   }
}

This code saves an association between the IProductCatalogRepository interface and an implementation class that is named CachedBdcProductCatalogRepository. The type mapping is saved in the content database using a property of the local SharePoint farm's property bag.

The SharePoint service locator allows the Partner Portal's components to be tested in isolation. Unit tests can replace the configured repository with a test-provided stand-in component. The Partner Portal unit tests use the MockProductCatalogRepository class for this purpose. The following code, taken from PartnerPortal\Contoso.PartnerPortal.ProductCatalog.Tests\ProductDetailsPresenterFixture.cs, demonstrates how the unit test replaces the configured component with a test-provided component.

SharePointServiceLocator.ReplaceCurrentServiceLocator(new ActivatingServiceLocator()
  .RegisterTypeMapping<IProductCatalogRepository, MockProductCatalogRepository>());

Note

It is a recommended practice to place repository interfaces and their implementations in separate assemblies. This makes it easier to replace implementations without affecting client components. Separate assemblies also make unit testing easier.
You should be aware that the Visual Studio extensions for Windows SharePoint Services version 1.3 requires that dependent assemblies be located in the global assembly cache. The reason for this is that Visual Studio extensions for Windows SharePoint Services 1.3 individually sends each assembly to the Web service to package. The packaging service iterates over all the types in the assembly. If one of the types has a base class or interface in a different assembly, this packaging fails because it cannot find them. When the assembly with the base class is in the global assembly cache, the packaging can continue normally.
The standard build process does not automatically install assemblies into the global assembly cache. Therefore, if you place interfaces or base classes in a separate assembly used by a Visual Studio extensions for Windows SharePoint Services project, you should add a post-build action to install your assembly into the global assembly cache.
For example, the Contoso.Common project includes the following post-build command:
call $(SolutionDir)..\Scripts\RegisterInGac.bat /u $(TargetName)
The RegisterInGac batch file locates the gacUtil.exe command-line utility program on the local system and uses this utility program to install the newly built assembly into the global assembly cache.

More Information

For more information about the SharePoint service locator, see The SharePoint Service Locator. For a walkthrough of the product data catalog, see Product Catalog.

Home page on MSDN | Community site