For example, let say I have 4 different entity that each implement a Add() method that add the entity to the database :
public class Profile
{
...
publi
You could get around that be the following; you just have to make sure at runtime it really is a TEntity:
public void Add()
{
object obj = this;
this._dbContext.Set<TEntity>().Add((TEntity)obj);
this._dbContext.SaveChanges();
}
Since the compiler loses track of what this is when you use an object type. If you get an error, it's because obj is not truly a TEntity. However, you may want to use a factory, repository, or other design pattern for working with the entity framework DBSet.
The solution to your problem is to be more explicit with the definition of the generic constraint. Define the constraint as TEntity must be a sub-class of Entity<TEntity> i.e. use where TEntity : Entity<TEntity>
instead of where TEntity : class
public abstract class Entity<TEntity> where TEntity : Entity<TEntity>
{
protected DbContext _dbContext;
protected Entity()
{
this._dbContext = new SMTDBContext();
}
public void Add()
{
this._dbContext.Set<TEntity>().Add((TEntity)this);
this._dbContext.SaveChanges();
}
}
Try a generic repository, at the end you will develop something similar. You need 3 interfaces:
And the implementations to those interfaces:
Here the code:
IEntity.cs
public interface IEntity<TId> where TId : IComparable
{
TId Id { get; set; }
}
IEntityContext.cs
public interface IEntityContext : IDisposable
{
void SetAsAdded<TEntity>(TEntity entity) where TEntity : class;
void SetAsModified<TEntity>(TEntity entity) where TEntity : class;
void SetAsDeleted<TEntity>(TEntity entity) where TEntity : class;
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}
IEntityRepository.cs
public interface IEntityRepository<TEntity, TId>
: IDisposable
where TEntity : class, IEntity<TId>
where TId : IComparable
{
IQueryable<TEntity> GetAll(
Expression<Func<TEntity, bool>> where = null,
Expression<Func<TEntity, object>> orderBy = null);
PaginatedList<TEntity> Paginate(int pageIndex, int pageSize);
TEntity GetSingle(TId id);
IQueryable<TEntity> GetAllIncluding(
Expression<Func<TEntity, bool>> where,
Expression<Func<TEntity, object>> orderBy,
params Expression<Func<TEntity, object>>[] includeProperties);
TEntity GetSingleIncluding(
TId id, params Expression<Func<TEntity, object>>[] includeProperties);
void Add(TEntity entity);
void Attach(TEntity entity);
void Edit(TEntity entity);
void Delete(TEntity entity);
int Save();
}
EntityRepository.cs
public class EntityRepository<TEntity, TId>
: IEntityRepository<TEntity, TId>
where TEntity : class, IEntity<TId>
where TId : IComparable
{
private readonly IEntityContext _dbContext;
public EntityRepository(IEntityContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
_dbContext = dbContext;
}
public IQueryable<TEntity> GetAllIncluding(
Expression<Func<TEntity, bool>> where,
Expression<Func<TEntity, object>> orderBy,
params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
IQueryable<TEntity> queryable = GetAll(where, orderBy);
foreach (Expression<Func<TEntity, object>> includeProperty in includeProperties)
{
queryable =
queryable.Include<TEntity, object>(includeProperty);
}
return queryable;
}
catch (Exception)
{
throw;
}
}
public TEntity GetSingleIncluding(
TId id,
params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
IQueryable<TEntity> entities =
GetAllIncluding(null, null, includeProperties);
TEntity entity =
Filter<TId>(entities, x => x.Id, id).FirstOrDefault();
return entity;
}
catch (Exception)
{
throw;
}
}
public void Add(TEntity entity)
{
try
{
_dbContext.Set<TEntity>().Add(entity);
if (this.EntityAdded != null)
this.EntityAdded(this, new EntityAddedEventArgs<TEntity, TId>(entity));
}
catch (Exception)
{
throw;
}
}
public void Attach(TEntity entity)
{
try
{
_dbContext.SetAsAdded(entity);
if (this.EntityAttach != null)
this.EntityAttach(this, new EntityAddedEventArgs<TEntity, TId>(entity));
}
catch (Exception)
{
throw;
}
}
public void Edit(TEntity entity)
{
try
{
_dbContext.SetAsModified(entity);
if (this.EntityModified != null)
this.EntityModified(this, new EntityModifiedEventArgs<TEntity, TId>(entity));
}
catch (Exception)
{
throw;
}
}
public void Delete(TEntity entity)
{
try
{
_dbContext.SetAsDeleted(entity);
if (this.EntityDeleted != null)
this.EntityDeleted(this, new EntityDeletedEventArgs<TEntity, TId>(entity));
}
catch (Exception)
{
throw;
}
}
public int Save()
{
try
{
return _dbContext.SaveChanges();
}
catch (Exception)
{
throw;
}
}
public IQueryable<TEntity> GetAll(
Expression<Func<TEntity, bool>> where = null,
Expression<Func<TEntity, object>> orderBy = null)
{
try
{
IQueryable<TEntity> queryable =
(where != null) ? _dbContext.Set<TEntity>().Where(where)
: _dbContext.Set<TEntity>();
return (orderBy != null) ? queryable.OrderBy(orderBy)
: queryable;
}
catch (Exception)
{
throw;
}
}
public TEntity GetSingle(TId id)
{
try
{
IQueryable<TEntity> entities = GetAll();
TEntity entity =
Filter<TId>(entities, x => x.Id, id).FirstOrDefault();
return entity;
}
catch (Exception)
{
throw;
}
}
public void Dispose()
{
_dbContext.Dispose();
}
#region Private
private IQueryable<TEntity> Filter<TProperty>(
IQueryable<TEntity> dbSet,
Expression<Func<TEntity, TProperty>> property, TProperty value)
where TProperty : IComparable
{
try
{
var memberExpression = property.Body as MemberExpression;
if (memberExpression == null ||
!(memberExpression.Member is PropertyInfo))
throw new ArgumentException
("Property expected", "property");
Expression left = property.Body;
Expression right =
Expression.Constant(value, typeof(TProperty));
Expression searchExpression = Expression.Equal(left, right);
Expression<Func<TEntity, bool>> lambda =
Expression.Lambda<Func<TEntity, bool>>(
searchExpression,
new ParameterExpression[] { property.Parameters.Single() });
return dbSet.Where(lambda);
}
catch (Exception)
{
throw;
}
}
private enum OrderByType
{
Ascending,
Descending
}
#endregion
}
EntityContext.cs
public abstract class EntityContext : DbContext, IEntityContext
{
/// <summary>
/// Constructs a new context instance using conventions to create the name of
/// the database to which a connection will be made. The by-convention name is
/// the full name (namespace + class name) of the derived context class. See
/// the class remarks for how this is used to create a connection.
/// </summary>
protected EntityContext() : base() { }
/// <summary>
/// Constructs a new context instance using conventions to create the name of
/// the database to which a connection will be made, and initializes it from
/// the given model. The by-convention name is the full name (namespace + class
/// name) of the derived context class. See the class remarks for how this is
/// used to create a connection.
/// </summary>
/// <param name="model">The model that will back this context.</param>
protected EntityContext(DbCompiledModel model) : base(model) { }
/// <summary>
/// Constructs a new context instance using the given string as the name or connection
/// string for the database to which a connection will be made. See the class
/// remarks for how this is used to create a connection.
/// </summary>
/// <param name="nameOrConnectionString">Either the database name or a connection string.</param>
public EntityContext(string nameOrConnectionString)
: base(nameOrConnectionString) { }
/// <summary>
/// Constructs a new context instance using the existing connection to connect
/// to a database. The connection will not be disposed when the context is disposed.
/// </summary>
/// <param name="existingConnection">An existing connection to use for the new context.</param>
/// <param name="contextOwnsConnection">
/// If set to true the connection is disposed when the context is disposed, otherwise
/// the caller must dispose the connection.
/// </param>
public EntityContext
(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection) { }
/// <summary>
/// Constructs a new context instance around an existing ObjectContext. An existing
/// ObjectContext to wrap with the new context. If set to true the ObjectContext
/// is disposed when the EntitiesContext is disposed, otherwise the caller must dispose
/// the connection.
/// </summary>
/// <param name="objectContext">An existing ObjectContext to wrap with the new context.</param>
/// <param name="EntitiesContextOwnsObjectContext">
/// If set to true the ObjectContext is disposed when the EntitiesContext is disposed,
/// otherwise the caller must dispose the connection.
/// </param>
public EntityContext(
ObjectContext objectContext,
bool EntityContextOwnsObjectContext)
: base(objectContext, EntityContextOwnsObjectContext)
{ }
/// <summary>
/// Constructs a new context instance using the given string as the name or connection
/// string for the database to which a connection will be made, and initializes
/// it from the given model. See the class remarks for how this is used to create
/// a connection.
/// </summary>
/// <param name="nameOrConnectionString">Either the database name or a connection string.</param>
/// <param name="model">The model that will back this context.</param>
public EntityContext(
string nameOrConnectionString,
DbCompiledModel model)
: base(nameOrConnectionString, model)
{ }
/// <summary>
/// Constructs a new context instance using the existing connection to connect
/// to a database, and initializes it from the given model. The connection will
/// not be disposed when the context is disposed. An existing connection to
/// use for the new context. The model that will back this context. If set
/// to true the connection is disposed when the context is disposed, otherwise
/// the caller must dispose the connection.
/// </summary>
/// <param name="existingConnection">An existing connection to use for the new context.</param>
/// <param name="model">The model that will back this context.</param>
/// <param name="contextOwnsConnection">
/// If set to true the connection is disposed when the context is disposed, otherwise
/// the caller must dispose the connection.
/// </param>
public EntityContext(
DbConnection existingConnection,
DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{ }
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
try
{
return base.Set<TEntity>();
}
catch (Exception)
{
throw;
}
}
public void SetAsAdded<TEntity>(TEntity entity) where TEntity : class
{
try
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Added;
}
catch (Exception)
{
throw;
}
}
public void SetAsModified<TEntity>(TEntity entity) where TEntity : class
{
try
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Modified;
}
catch (Exception)
{
throw;
}
}
public void SetAsDeleted<TEntity>(TEntity entity) where TEntity : class
{
try
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Deleted;
}
catch (Exception)
{
throw;
}
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (Exception)
{
throw;
}
}
public new void Dispose()
{
try
{
base.Dispose();
}
catch (Exception)
{
throw;
}
}
#region Private
private DbEntityEntry GetDbEntityEntrySafely<TEntity>(
TEntity entity) where TEntity : class
{
try
{
DbEntityEntry dbEntityEntry = base.Entry<TEntity>(entity);
if (dbEntityEntry.State == EntityState.Detached)
Set<TEntity>().Attach(entity);
return dbEntityEntry;
}
catch (Exception)
{
throw;
}
}
#endregion
}
Long Answer but worth it... Have a nice day :) Its part of a personal huge project :D