Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have some entities in use in my project, and to make things easier, I would like to have the type of the key for that entity defined via a generic. E.g.:

public abstract class Entity<T>
{
    public virtual T Id { get; set; }
}

This way, I can pull the Id field into the base entity and not have to define it every time (There's a few other fields on Entity in the real system, so there is other utility to doing this)

Then, since I'm using Fluent NHibernate, I make a generic mapping class to go along with this generic entity:

internal abstract class EntityMapping<T,TK> : ClassMap<T> where T : Entity<TK>
{
    protected EntityMapping()
    {
        Id(m => m.Id);
    }
}   

And starting here is when I wonder if there's a better way of handling this. C# doesn't seem to infer the TK type parameter from the Entity definition, and having to add to all of my mapping classes the key parameter for the entity they map seems redundant, all the more so since it also extends to some of the higher generic data access classes, e.g.:

public abstract class EntityRepository<T,TK> where T : Entity<TK> { }

Is there a tidier way of handling this, or is this what I have to do if I want to have the key type of my entities passed in?

share|improve this question
3  
I think there is no tidier way, as long as you want to keep everything generic. –  svick Jan 30 '13 at 10:28
    
That's what I was thinking was probably the case. Worth asking though. –  Matt Sieker Jan 30 '13 at 17:53
    
I would suggest that you use marker interfaces for generic type constraints (ex. IEntity, IEntity<TId> : IEntity, IRepository, IRepository<T> : IRepository where T: class, IEntity, new()) . A base entity class as shown below should work fine as well, but use marker interfaces instead for constraints to allow using your base class or a custom class. –  Shelakel Feb 10 '13 at 17:19
add comment

2 Answers 2

There is no way to simplify generic parameters if you want to keep Entity class generic, as noted by @svick.

The only thing I can suggest as alternative here is to remove generic parameter from Entity. I don't think you need 10 different types for Id field, most likely you'll have int Id or Guid Id, so you can create

public interface IEntity<T>
{
    T Id { get; set; }
}

public abstract class IntEntity: IEntity<int>
{
    public virtual int Id { get; set; }
}

public abstract class GuidEntity: IEntity<Guid>
{
    public virtual Guid Id { get; set; }
}

Not sure if you need IEntity<T> interface, added it so that you don't loose the possibility to reference both types of entities in generic manner.

Yes, you'll have to declare separate IntEntityRepository and GuidEntityRepository, but I don't think it's that hard given that you can extract common code to base class, and it's a one-time job.

As to mappings - I don't use class hierarchy for them (declaring all the fields on the actual entity mapper), but you can do the same trick as with repositories.

share|improve this answer
add comment

This is quite similar to something I'm doing. If I may give a tiny bit of advice for your Entity class, it would be to override/define some of the standard object operations as such:

public abstract class Entity<T>
{
    public virtual T Id { get; set; }

    public static bool operator ==(Entity entity1, Entity entity2)
    {
        return ReferenceEquals(entity1, entity2) || (((object)entity1 != null) && entity1.Equals(entity2));
    }

    public static bool operator !=(Entity entity1, Entity entity2)
    {
        return !(entity1 == entity2);
    }

    public override bool Equals(object obj)
    {
        var entity = obj as Entity;

        return (entity != null) && (entity.GetType() == this.GetType()) && (entity.Id == this.Id);
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }

    public override string ToString()
    {
        return this.Id.ToString(CultureInfo.InvariantCulture);
    }
}
share|improve this answer
1  
I had Equals and GetHashCode overridden on the actual class (I just pulled out the relevant properties for this question), so IComparer/IEquatable would work. The operator overloading is a good idea though. –  Matt Sieker Feb 1 '13 at 16:47
    
@MattSieker rock on, sir! –  Jesse C. Slicer Feb 1 '13 at 16:52
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.