如何在EF6 Code First中使用generics类型与数据库上下文

例如,假设我有4个不同的实体,每个实体都实现一个将实体添加到数据库的Add()方法:

public class Profile { ... public void Add() { this._dbContext.Profile.Add(this); this._dbContext.SaveChanges(); } ... } 

现在我希望有一个generics类在一个抽象类而不是X个类中实现这种行为。 所以我尝试了以下内容:

 public abstract class Entity where TEntity : class { protected DbContext _dbContext; protected Entity() { this._dbContext = new SMTDBContext(); } public void Add() { this._dbContext.Set().Add(this); this._dbContext.SaveChanges(); } } 

当然它并不是因为“这个”不是一个TEntity ……但它将在未来! 到目前为止,我试图寻找做类似事情的人没有成功。

您的问题的解决方案是使用generics约束的定义更明确。 定义约束,因为TEntity必须是Entity 的子类,即使用where TEntity : Entity而不是where TEntity : class

 public abstract class Entity where TEntity : Entity { protected DbContext _dbContext; protected Entity() { this._dbContext = new SMTDBContext(); } public void Add() { this._dbContext.Set().Add((TEntity)this); this._dbContext.SaveChanges(); } } 

尝试一个通用的存储库,最后你会开发类似的东西。 你需要3个接口:

  • IEntity
  • IEntityRepository
  • IEntityContext

以及这些接口的实现:

  • 的EntityContext
  • EntityRepository

这里的代码:

IEntity.cs

 public interface IEntity where TId : IComparable { TId Id { get; set; } } 

IEntityContext.cs

 public interface IEntityContext : IDisposable { void SetAsAdded(TEntity entity) where TEntity : class; void SetAsModified(TEntity entity) where TEntity : class; void SetAsDeleted(TEntity entity) where TEntity : class; IDbSet Set() where TEntity : class; int SaveChanges(); } 

IEntityRepository.cs

 public interface IEntityRepository : IDisposable where TEntity : class, IEntity where TId : IComparable { IQueryable GetAll( Expression> where = null, Expression> orderBy = null); PaginatedList Paginate(int pageIndex, int pageSize); TEntity GetSingle(TId id); IQueryable GetAllIncluding( Expression> where, Expression> orderBy, params Expression>[] includeProperties); TEntity GetSingleIncluding( TId id, params Expression>[] includeProperties); void Add(TEntity entity); void Attach(TEntity entity); void Edit(TEntity entity); void Delete(TEntity entity); int Save(); } 

EntityRepository.cs

 public class EntityRepository : IEntityRepository where TEntity : class, IEntity where TId : IComparable { private readonly IEntityContext _dbContext; public EntityRepository(IEntityContext dbContext) { if (dbContext == null) throw new ArgumentNullException("dbContext"); _dbContext = dbContext; } public IQueryable GetAllIncluding( Expression> where, Expression> orderBy, params Expression>[] includeProperties) { try { IQueryable queryable = GetAll(where, orderBy); foreach (Expression> includeProperty in includeProperties) { queryable = queryable.Include(includeProperty); } return queryable; } catch (Exception) { throw; } } public TEntity GetSingleIncluding( TId id, params Expression>[] includeProperties) { try { IQueryable entities = GetAllIncluding(null, null, includeProperties); TEntity entity = Filter(entities, x => x.Id, id).FirstOrDefault(); return entity; } catch (Exception) { throw; } } public void Add(TEntity entity) { try { _dbContext.Set().Add(entity); if (this.EntityAdded != null) this.EntityAdded(this, new EntityAddedEventArgs(entity)); } catch (Exception) { throw; } } public void Attach(TEntity entity) { try { _dbContext.SetAsAdded(entity); if (this.EntityAttach != null) this.EntityAttach(this, new EntityAddedEventArgs(entity)); } catch (Exception) { throw; } } public void Edit(TEntity entity) { try { _dbContext.SetAsModified(entity); if (this.EntityModified != null) this.EntityModified(this, new EntityModifiedEventArgs(entity)); } catch (Exception) { throw; } } public void Delete(TEntity entity) { try { _dbContext.SetAsDeleted(entity); if (this.EntityDeleted != null) this.EntityDeleted(this, new EntityDeletedEventArgs(entity)); } catch (Exception) { throw; } } public int Save() { try { return _dbContext.SaveChanges(); } catch (Exception) { throw; } } public IQueryable GetAll( Expression> where = null, Expression> orderBy = null) { try { IQueryable queryable = (where != null) ? _dbContext.Set().Where(where) : _dbContext.Set(); return (orderBy != null) ? queryable.OrderBy(orderBy) : queryable; } catch (Exception) { throw; } } public TEntity GetSingle(TId id) { try { IQueryable entities = GetAll(); TEntity entity = Filter(entities, x => x.Id, id).FirstOrDefault(); return entity; } catch (Exception) { throw; } } public void Dispose() { _dbContext.Dispose(); } #region Private private IQueryable Filter( IQueryable dbSet, Expression> 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> lambda = Expression.Lambda>( 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 { ///  /// 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. ///  protected EntityContext() : base() { } ///  /// 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. ///  /// The model that will back this context. protected EntityContext(DbCompiledModel model) : base(model) { } ///  /// 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. ///  /// Either the database name or a connection string. public EntityContext(string nameOrConnectionString) : base(nameOrConnectionString) { } ///  /// 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. ///  /// An existing connection to use for the new context. ///  /// If set to true the connection is disposed when the context is disposed, otherwise /// the caller must dispose the connection. ///  public EntityContext (DbConnection existingConnection, bool contextOwnsConnection) : base(existingConnection, contextOwnsConnection) { } ///  /// 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. ///  /// 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. ///  public EntityContext( ObjectContext objectContext, bool EntityContextOwnsObjectContext) : base(objectContext, EntityContextOwnsObjectContext) { } ///  /// 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. ///  /// Either the database name or a connection string. /// The model that will back this context. public EntityContext( string nameOrConnectionString, DbCompiledModel model) : base(nameOrConnectionString, model) { } ///  /// 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. ///  /// 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. ///  public EntityContext( DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection) { } public new IDbSet Set() where TEntity : class { try { return base.Set(); } catch (Exception) { throw; } } public void SetAsAdded(TEntity entity) where TEntity : class { try { DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity); dbEntityEntry.State = EntityState.Added; } catch (Exception) { throw; } } public void SetAsModified(TEntity entity) where TEntity : class { try { DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity); dbEntityEntry.State = EntityState.Modified; } catch (Exception) { throw; } } public void SetAsDeleted(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 entity) where TEntity : class { try { DbEntityEntry dbEntityEntry = base.Entry(entity); if (dbEntityEntry.State == EntityState.Detached) Set().Attach(entity); return dbEntityEntry; } catch (Exception) { throw; } } #endregion } 

很长的答案,但值得…有一个美好的一天:)它的个人巨大项目的一部分:D

你可以解决这个问题: 你只需要在运行时确保它真的是一个TEntity:

 public void Add() { object obj = this; this._dbContext.Set().Add((TEntity)obj); this._dbContext.SaveChanges(); } 

由于编译器在使用对象类型时失去了对它的跟踪。 如果你收到错误,那是因为obj不是真正的TEntity。 但是,您可能希望使用工厂,存储库或其他设计模式来处理entity frameworkDBSet。