简体   繁体   English

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

[英]How to use generic type with the database Context in EF6 Code First

For example, let say I have 4 different entity that each implement a Add() method that add the entity to the database : 例如,假设我有4个不同的实体,每个实体都实现一个将实体添加到数据库的Add()方法:

public class Profile
{
    ...

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

    ...
}

Now I would like to have a generic class that implement this kind of behavior in one abstract class instead of X number of classes. 现在我希望有一个泛型类在一个抽象类而不是X个类中实现这种行为。 So I tried the following : 所以我尝试了以下方法:

public abstract class Entity<TEntity> where TEntity : class 
{
    protected DbContext _dbContext;

    protected Entity()
    {
        this._dbContext = new SMTDBContext();
    }

    public void Add()
    {
        this._dbContext.Set<TEntity>().Add(this);
        this._dbContext.SaveChanges();
    }
}

Of course it doesnt worrk because "this" is not a TEntity... but it will be in the future! 当然它并不是因为“这个”不是一个TEntity ......但它将在未来! I tried searching for someone who did something similar without success so far. 到目前为止,我试图寻找做类似事情而没有成功的人。

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> ie use where TEntity : Entity<TEntity> instead of where TEntity : class 定义约束,因为TEntity必须是Entity <TEntity>的子类,即使用where TEntity : Entity<TEntity>而不是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: 你需要3个接口:

  • IEntity IEntity
  • IEntityRepository IEntityRepository
  • IEntityContext IEntityContext

And the implementations to those interfaces: 以及这些接口的实现:

  • EntityContext 的EntityContext
  • EntityRepository EntityRepository

Here the code: 这里的代码:

IEntity.cs IEntity.cs

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

IEntityContext.cs 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 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 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 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 很长的答案,但值得...有一个愉快的一天:)它的个人巨大项目的一部分:D

You could get around that be the following; 你可以解决以下问题; you just have to make sure at runtime it really is a TEntity: 你只需要在运行时确保它真的是一个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. 如果你收到错误,那是因为obj不是真正的TEntity。 However, you may want to use a factory, repository, or other design pattern for working with the entity framework DBSet. 但是,您可能希望使用工厂,存储库或其他设计模式来处理实体框架DBSet。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM