如何在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。