I've already implemented my Repository classs, but I'm wondering how I can refactor it's methods into a base class that can be extended by different repository types.
I started by creating the base Repository class below, but not sure how abstract each method should be. Also how should I replace the Model type with a generic placeholder.
In this example, the abstract methods contain only the method definition and not the implementation:
public abstract Array sort(Array arr);
Can anyone advise on how to refactor the methods for generics?
I started by creating the base abstract Repository class, but got stuck at replacing the methods with generic Types and generic parameters.
The example below is Delete()
which is specific to the CustomerModel. It should be generic for easier re-use of the class:
public abstract class BaseRepository
{
public async Task DeleteCustomer(CustomerModel customer)
{
var collection = StartConnection();
var filter = Builders<CustomerModel>.Filter.Where(x => x.Id == customer.Id);
var result = await collection.DeleteOneAsync(filter);
customers.Remove(customer);
}
}
So for example this is the complete CustomerRepository class, which contains CRUD operations for my remote db. The methods are all specific to the CustomerModel which makes it hard to re-use:
public class CustomerRepository : ICustomerRepository
{
private static List<CustomerModel> customers = new List<CustomerModel>();
static CustomerRepository()
{
}
private CustomerRepository()
{
}
public static CustomerRepository Instance
{
get
{
return instance;
}
}
public CustomerModel GetACustomer()
{
if (customers == null)
LoadCustomers();
return customers.FirstOrDefault();
}
public List<CustomerModel> GetCustomers()
{
if (customers.Count == 0)
LoadCustomers();
return customers;
}
public CustomerModel GetCustomerById(ObjectId id)
{
if (customers == null)
LoadCustomers();
return customers.Where(c => c.Id == id).FirstOrDefault();
}
public CustomerModel GetCustomerByEmail(string email)
{
if (customers == null)
LoadCustomers();
return customers.Where(c => c.Email == email).FirstOrDefault();
}
public async Task DeleteCustomer(CustomerModel customer)
{
var collection = StartConnection();
var filter = Builders<CustomerModel>.Filter.Where(x => x.Id == customer.Id);
var result = await collection.DeleteOneAsync(filter);
customers.Remove(customer);
}
public async Task AddCustomer(CustomerModel customer)
{
var collection = StartConnection();
await collection.InsertOneAsync(customer);
customers.Add(customer);
}
public async Task UpdateCustomer(CustomerModel customer)
{
var collection = StartConnection();
var filter = Builders<CustomerModel>.Filter.Where(x => x.Id == customer.Id);
collection.Find(filter).ToString();
var result = await collection.ReplaceOneAsync(filter, customer, new UpdateOptions { IsUpsert = true });
var index = customers.FindIndex(a => a.Id == customer.Id);
customers[index] = customer;
}
private void LoadCustomers()
{
var collection = StartConnection();
try
{
customers = collection.Find(new BsonDocument()).ToListAsync().GetAwaiter().GetResult();
}
catch (MongoException ex)
{
//Log exception here:
MessageBox.Show("A connection error occurred: " + ex.Message, "Connection Exception", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
private static IMongoCollection<CustomerModel> StartConnection()
{
var client = new MongoClient(connectionString);
var database = client.GetDatabase("orders");
//Get a handle on the customers collection:
var collection = database.GetCollection<CustomerModel>("customers");
return collection;
}
}
Do not make a base class containing entity specific methods. Make base generic from beginning.
public class Repository<T> where T : new()
{
public async Task<T> GetAsync(object key)
{}
public async Task DeleteAsync(T t)
{}
}
Add any level of abstraction you feel like. If you are using e.g. some kind of ORM within your repo this could be enough.
You can't change the method names in inheriting classes. And it's more straight forward to use repos if all have same methods for operations.
If you can use base as is, then only Type
parameter changes. So you'd instantiate e.g. Repository<Customer>
in your code.
If logic differs between entities, then have e.g. CustomerRepository : Repository<Customer>
and put logic there. Also, mark base as abstract and methods abstract/virtual.