Reputation: 13
My scenario:
public class EntityBase
{
public int ID { get; set; }
[Required()]
public string Name { get; set; }
//And this is what is getting me
//I want a "Type" enum
}
Then derived classes would have different enums that they would assign to Type.
public class AnimalEntity : EntityBase
{
//Type would have an 'animal type' value: Land, Sea or Air
//Implementation code would do something like:
// myAnimal.Type = AnimalType.Land
}
public class PersonEntity : EntityBase
{
//Type would have a 'person type' value: Doctor, Lawyer or Engineer
//Implementation code would do something like:
// myPerson.Type = PersonType.Lawyer
}
public class MonsterEntity : EntityBase
{
//Type would have a 'monster type' value: Goblinoid, Undead
}
So, the big question is what am I trying to do, right? I am trying to create a base repository class, which will return entities grouped by type. All my entities will have some kind of "type", and I want to create a generic "group by type".
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : EntityBase
{
//Our common GetAsync, GetByIdAsync, and all our other CRUD
//And then something like this:
public IEnumerable<GroupedData<string, T>> GetGroupedByType(string searchTerm)
{
var entities =
from s in DbSet
where (searchTerm == null || s.Name.ToLower().Contains(searchTerm))
group s by s.Type into g
select new GroupedData<string, T> { Key = g.Key.ToString(), Data = g };
return (entities);
}
}
When T is AnimalEntity, I would get groups Land, Sea and Air with the corresponding entities. For PersonEntity, I would get Doctor, Lawyer, Engineer groups.
If my approach/design is invalid or less than ideal, please let me know.
Upvotes: 1
Views: 174
Reputation: 67090
Enum (please pardon me) are kind of second class citizens so first thing you may think about will not work:
class EntityBase<T> where T : enum {
public T Type { get; set; }
}
Unfortunately it doesn't compile, you may then think to replace enum
with a base class:
class EntityBase<T> where T : EntityTypeBase {
public T Type { get; set; }
}
Implementing in EntityTypeBase
everything you need to be comfortable with them (==
and !=
operators, IConvertible
interface and other boilerplate). It's a lot of code and you'll need also to manage that in EF (otherwise you won't be able to use such property in your queries unless you load everything in memory as objects). You may also force the use of enum
s (with a run-time check) but this will break SQL code generation in EF.
What's I'd suggest in this case is to use a type EF knows and understand. You may use a string (if you wish so) or an integer (as in this example):
class EntityBase
public virtual int Type { get; set; }
}
In a derived class:
class AnimalEntity : EntityBase {
public override int Type {
get { return base.Type; }
set {
if (!Enum.IsDefined(typeof(AnimalType), value))
throw new ArgumentException();
base.Type = (int)value;
}
}
}
In this way you still can use PersonType.Layer
and AnimalType.Land
keeping also a little of type safety. Of course you need to keep your enums in-sync to do not have duplicated values (otherwise group by
won't work).
As last please also consider to use...another entity. If you have another table EntityType
:
ID Name ApplicableTo 0 Laywer Person 1 Programmer Person 2 Land Animal ...
What you have to do in the setter is to check if type is applicable or not and you may have few convenience classes that will group them by type:
public static class PersonType {
public static EntityType Lawyer { get { ... } }
public static EntityType Programmer { get { ... } }
}
IMO this is scale better (easier to add new items and you can delegate, in future, some behavior to EntityType
items) and it is safer than hard-coded constants (because integrity is granted by DB engine itself). Of course price to pay is extra overhead for the search in the EntityType
table (unless you use some caching mechanism).
Upvotes: 1
Reputation: 156978
Two options I can think of:
First, preferably, use a generic type parameter (T
in this sample):
public class EntityBase<T>
{
public T Type {get;set;}
}
Supply that type in the type declaration:
public class AnimalEntity : EntityBase<AnimalEnum>
{ }
Second, if you need more freedom, I usually use a list of string
contants:
public class EntityBase
{
public string Type {get;set;}
}
public static class AnimalTypes
{
public const string Dog = "dog";
public const string Cat = "cat";
}
Upvotes: 1