5
\$\begingroup\$

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?

asked Jan 30, 2013 at 5:40
\$\endgroup\$
3
  • 3
    \$\begingroup\$ I think there is no tidier way, as long as you want to keep everything generic. \$\endgroup\$ Commented Jan 30, 2013 at 10:28
  • \$\begingroup\$ That's what I was thinking was probably the case. Worth asking though. \$\endgroup\$ Commented Jan 30, 2013 at 17:53
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Feb 10, 2013 at 17:19

2 Answers 2

4
\$\begingroup\$

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.

answered Jan 31, 2013 at 9:36
\$\endgroup\$
2
  • \$\begingroup\$ What about a composite key ? \$\endgroup\$ Commented Oct 28, 2018 at 20:30
  • \$\begingroup\$ I don't think it's reasonable to create generic base classes for entities with composite keys. In case of a single key, it's ok to assume (or define a convention) that key's name would be "Id". I don't think you would have such a convention for generic composite keys... \$\endgroup\$ Commented Oct 30, 2018 at 12:13
0
\$\begingroup\$

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> where T: IEquatable<T>
{
 protected Entity(T id) => this.Id = id;
 public virtual T Id { get; }
 public static bool operator ==(Entity<T> entity1, Entity<T> entity2) =>
 ReferenceEquals(entity1, entity2) || (((object)entity1 != null) && entity1.Equals(entity2));
 public static bool operator !=(Entity<T> entity1, Entity<T> entity2) => !(entity1 == entity2);
 public override bool Equals(object obj)
 {
 Entity<T> entity = obj as Entity<T>;
 return (entity != null) && (entity.GetType() == this.GetType()) && entity.Id.Equals(this.Id);
 }
 public override int GetHashCode() => this.Id?.GetHashCode() ?? 0;
 public override string ToString() => this.Id?.ToString() ?? string.Empty;
}
answered Feb 1, 2013 at 15:58
\$\endgroup\$
4
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Feb 1, 2013 at 16:47
  • \$\begingroup\$ @MattSieker rock on, sir! \$\endgroup\$ Commented Feb 1, 2013 at 16:52
  • 1
    \$\begingroup\$ @JesseC.Slicer code does not work... missing Entity Base class or must be of type Entity<T> \$\endgroup\$ Commented Oct 25, 2015 at 16:07
  • \$\begingroup\$ @Beachwalker it worked 2 1/2 years ago. \$\endgroup\$ Commented Oct 25, 2015 at 16:17

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.