Entity<TEntity, TKey> is base class for an entity TEntity with unique identifier TKey.
Properties:
Methods:
Entity<TEntity, TKey> also have implementation of operators == and !=.
Example of simple entity:
// User entity with unique identifier property "int Id { get; protected set; }".publicclassUser:Entity<User,int>{publicstring Name { get; privateset; }privateUser() : base() { }publicUser(int id,string name) : base() { Id = id; Name = name; }}
Let's compare two object of type User:
bool isEqual;// Entities with different identifiers:var user1 =newUser(1,"Robert");var user2 =newUser(2,"Robert");isEqual = user1 == user2; // Returns "false" because "user1.Id" equals "1", but "user2.Id" equals "2".// Entities with same identifiers:var user3 =newUser(3,"Emma");var user4 =newUser(3,"Leo");isEqual = user3 == user4; // Returns "true" because their identifiers are equals.
You can also use enum, Guid, string and other types for identifier:
publicenumRoleId{ Guest, Admin}publicclassRole:Entity<Role,RoleId>{publicstring Name { get; privateset; }privateRole() : base() { }publicRole(RoleId id,string name) :this() { Id = id; Name = name; }}
In real world when you using ORM (like EF6 or EFCore) on board you don't setting entity identifier in constructor for auto-increment primary keys. It's because ORM do it when you call SaveChanges(). That's why you may need method IsTransient().
Example:
var user =newUser("Olaf");int currentUserId =user.Id; // Returns "0", because you don't set identifier in constructor;bool isTransient =user.IsTransient(); // Returns "true"// Use DbContext to save user in databaseusing(var dbContext =newUserDbContext()){dbContext.Users.Add(user);dbContext.SaveChanges();}int currentUserId =user.Id; // Returns "1", because auto-incremented;bool isTransient =user.IsTransient(); // Returns "false"
CompositeEntity
CompositeEntity<TEntity> is base class for an entity TEntity with composite unique identifier.
CompositeEntity<TEntity> have all the same methods as Entity<TEntity, TKey>, but don't have and Id property. Instead of public property Id that class have protected abstract method GetCompositeId() that should be implementing to resolve composite identifier.
Using this class is very helpful when setting up entity type configurations in EF6 or EFCore DbContext.
Example:
publicclassUserRole:CompositeEntity<UserRole>{publicint UserId { get; privateset; }publicvirtualUser User { get; privateset; }publicRoleId RoleId { get; privateset; }publicvirtualRole Role { get; privateset; }privateUserRole() : base() { }publicUserRole(User user,Role role) :this() { UserId =user.Id; User = user; RoleId =role.Id; Role = role; } // Trick for setting up entity type configuration in ORM DbContext.publicstaticExpression<Func<UserRole,object>> CompositeId=> x =>new { x.UserId,x.RoleId };protectedoverrideobjectGetCompositeId()=>CompositeId.Compile().Invoke(this);}
And then simply setting up entity type configuration in DbContext, for example EFCore:
internalclassUserRoleConfiguration:IEntityTypeConfiguration<UserRole>{publicvoidConfigure(EntityTypeBuilder<UserRole> builder) { // Use tricky property.builder.HasKey(UserRole.CompositeId);/* Other settings */ }}
Auto-auditing
Structr.Domain provides some abstract classes and interfaces to easy implement auto-auditing in ORM DbContext.
Auto-auditing - it's when you call SaveChanges() entity's properties like DateCreated or DateModified filling and tracking automatically.
List of auditable interfaces and abstract classes: