What is SharpRepository?
I think this quote sums it up nicely, “SharpRepository is amazing! I couldn’t have written the Gettysburg Address without it. Plus it picked out this awesome hat.” – Abraham Lincoln
In reality, SharpRepository is a generic repository layer with many persistence implementations like EntityFramework, RavenDB, MongoDB, and InMemory to name a few. SharpRepository abstracts away the technical concerns of your data layer by offering a common interface for dealing with your domain objects. In non-technical jargon, it allows you to add, update, delete and find data that is stored somewhere else, where, it doesn’t really matter. That is the beauty of it.
In this post, I will describe how to get up and running with SharpRepository and basic usage. In future posts we will get into more advanced usage scenarios like caching, batching, testing/mocking, as well as get into the specifics of the different type of specific repository implementations.
Today, my code samples will be using the InMemoryRepository (which as it’s name suggests, stores everything in memory and therefore is only really useful for testing and prototyping) but it can easily be swapped out for the Ef5Repository or RavenDbRepository. The only change is to how that repository is instantiated.
Installation
First off you’ll need to get the SharpRepository bits. There are two ways you can go about this. Either download the source from GitHub or use one of the SharpRepository NuGet packages. NuGet is the easiest and quickest way, though if you are looking for the one of the following implementations you will need to get it from source currently (though this will change in the future I’m sure): Db4o, MongoDB, RavenDB or XML, with CouchDB in development as we speak.
In Visual Studio, open the Manage NuGet Packages dialog (right click on References in your project) and do a search for SharpRepository to see the available packages. We decided to break SharpRepository down into multiple packages so you could only install what you need. You’ll first want to choose which implementation you want and install that. It will bring along the base SharpRepository package as well that is common across the board. For our purposes we’ll choose “SharpRepository for InMemory” and click Install.
Instantiating your Repository
public class Order { public int OrderId { get; set; } public string Name { get; set; } } var orderRepository = new InMemoryRepository<Order>();
This is the simplest way to create your repository. It creates a repository for accessing Orders. Pretty straight forward.
In order for SharpRepository to do the basic CRUD operations it needs to know what the primary key of the entity is.
You can see above that I didn’t define that anywhere. So there are a few things that SharpRepository is taking care of for us here. If there is no primary key type provided then it defaults to int. It is also figuring out which property is the primary key based on convention. It will automatically find a property with the name Id or [EntityName]Id. So since we have a OrderId property on our Order entity it can find it automatically without help from us. So what if we aren’t in this simple scenario?
public class User { public Guid UserId { get; set; } public string FullName { get; set; } public string Email { get; set; } } public class Item { [RepositoryPrimaryKey] public int ItemKey { get; set; } public string Title { get; set; } } var userRepository = new InMemoryRepository<User, Guid>(); var itemRepository = new InMemoryRepository<Item>(); // or <Item, int>
As you can see above, if the Primary Key is not an int, you need to define what that type is in the declaration, and if the primary key property name does not follow conventions you can tag it with the RepositoryPrimaryKey attribute to let SharpRepository know which one to use.
CRUD operations
Now that we’ve created the repository let’s actually use it. The easiest way is to show some code.
// Create var create = new Order { Name = "Big sale" }; orderRepository.Add(create); var orderId = create.OrderId; // Read var read = orderRepository.Get(orderId); // Update read.Name = "Really big sale"; orderRepository.Update(read); // Delete orderRepository.Delete(read);
I think that pretty much speaks for itself.
Querying Data
CRUD operations are great and all but you are going to need to run some queries also. Here is some code showing some of the various ways to query.
// Find var find = orderRepository.Find(x => x.Name == "Big sale"); // Add multiple new orders var newOrders = new List<Order> { new Order { Name = "New Order 2" }, new Order { Name = "New Order 3" }, new Order { Name = "New Order 4" } }; orderRepository.Add(newOrders); // FindAll var orders = orderRepository.FindAll(x => x.Name.StartsWith("New Order")); orders = orderRepository.FindAll(x => x.OrderId < 3); // GetAll var allOrders = orderRepository.GetAll();
As you can see, there is a Find method that returns a single entity, FindAll returns all matching entities as a List, and GetAll which returns every entity.
When querying you are likely to need to do some sorting or pagination. The SharpRepository FindAll and GetAll methods can accept an optional IQueryOptions parameter which can be used for sorting or paginating (is that a word?).
var pageNumber = 2; var pageSize = 10; // sort by FullName using an expression // (the string generic type is the type of the property to sort on) var queryOptions = new PagingOptions<User, string>(pageNumber, pageSize, x => x.FullName); // sort by Name column descending passing a string for the property // (useful when passing the sorting from the UI) var queryOptions2 = new PagingOptions<User>(pageNumber, pageSize, "FullName", true); var usersForPageTwo = userRepository.GetAll(queryOptions);
SharpRepository also provides the ability to map your entity to a new returned type. This is useful in many situations. Sometimes you just want to return only the actual columns you need, or you can use it to map directly to your ViewModel from your Entity Model that you are querying. You can achieve this by providing an Expression to any of the query methods. We call this parameter selector because you can think of it as using the .Select() LINQ method and you provide the same type of Expression.
// find the names of all Users with a gmail.com email address, sorted by their name var gmailUsers = userRepository.FindAll(x => x.Email.EndsWith("@gmail.com"), x => x.FullName, // only return the name new SortingOptions<Person>("FullName"));
In closing
If you make use of the Repository pattern, or want to start, you should check out SharpRepository. It provides a lot of benefit right out of the box and can be used across many different persistence implementations. I’ve just covered the basics in this post and in future posts we’ll get into the provided caching benefits SharpRepository provides as well as batching and testing.