Reputation: 12737
I have several classes in my project that are mapped to table rows in a database.
Most of the classes share a lot of the boiler plate code to fetch data from database FetchAll
, and to return certain object/row based on a given id FetchById()
.
I am thinking of writing a single class that can perform FetchAll
and FetchById()
and then all of my other classes can inherit from this object. However, I am struggling to define the base class to be generic. Other classes can't inherit of it and at the same time be of the generic type associated with what they are inheriting from.
To better explain my question, I will start simplifying two of my common classes that share similar code:
class Person {
public int Id { get; set; }
public string Name { get; set; }
public static List<Person> All {
get { /* fetch all rows and */ return List<Person>; }
}
public static Person FetchById(int id) {
return All.Where(p => p.Id == id).SingleOrDefault();
}
}
class Department {
public int Id { get; set; }
public string Name { get; set; }
public string CostCenter { get; set; }
public static List<Department> All { get { /* same logic as with Person */ } }
public static Department FetchById(int id) { /* same logic as with Person */ }
}
Now here is what I tried to do to reduce code duplication:
public interface IIdentifiable {
// make sure all objects have an Id property
int Id { get; set; }
}
class DbObject<T> where T: IIdentifiable {
public static List<T> All { get { /* return List<T>; */ } }
public static T FetchById(int id) {
return All.Where(object => object.Id == id).FirstOrDefault();
}
}
Now I went back to my Person
and Department
classes to take advantage of DbObject
:
class Person : DbObject<Person>, IIdentifiable // won't compile: The type cannot be used as type parameter T
{
public int Id { get; set; }
public string Name { get; set; }
// public static List<Person> All {get{ ... }}; // should be inherited
// public static Person FetchById(int id) { ... } // should be inherited
}
I did the same with Department
. You get the idea.
I feel I am doing something wrong. I know it should be much simpler than this.
How can I make DbObject
generic and inheritable at the same time?
Upvotes: 1
Views: 65
Reputation: 117174
You can do this by adopting the Curiously recurring template pattern.
Effectively you define DbObject<T>
such that T : DbObject<T>
then it works.
public class DbObject<T> where T : DbObject<T>, IIdentifiable
{
public List<T> All { get { return new List<T>(); } }
public T FetchById(int id)
{
return All.Where(x => x.Id == id).FirstOrDefault();
}
}
public class Person : DbObject<Person>, IIdentifiable
{
public int Id { get; set; }
public string Name { get; set; }
}
Now this code works:
Person p = new Person();
List<Person> all = p.All;
In your original code you were using static
methods. I assume that is so you could write List<Person> all = Person.All;
, but you can't inherit static
methods.
You might want to consider making a repository object that is responsible for returning the people.
Do keep in mind that the downside to this approach is that you need to be careful. This code compiles, but would probably not be what you want:
public class Department : DbObject<Person>, IIdentifiable
{
public int Id { get; set; }
public string Name { get; set; }
}
Upvotes: 5
Reputation: 11173
You can use recursive generics and static methods.
public abstract class Identified<T> where T : Identified<T>{
public static List<T> GetAll(){...}
}
public class Person:Identified<Person>{...}
var allPeople = Person.GetAll();
You shouldn't need to redefine GetAll
in each subtype. But I would recommend that FetchById
performs a separate query. Do you really want to load the entire table to view one record?
But I would actually recommend a separate generic factory class. It's difficult to configure and test static methods.
Upvotes: 0