5

有没有办法从 DbSet 继承?我想添加一些新属性,如下所示:

public class PersonSet : DbSet<Person>
{
    public int MyProperty { get; set; }
}

但我不知道如何在我的 DbContext 中实例化它

public partial MyContext : DbContext
{
    private PersonSet _personSet; 
    public PersonSet PersonSet
    {
        get 
        {
            _personSet = Set<Person>(); // Cast Error here
            _personSet.MyProperty = 10;
            return _personSet;
        }
    }
}

我怎样才能做到这一点?

4

4 回答 4

5

一种解决方案是创建一个实现 IDbSet 并将所有操作委托给真实 DbSet 实例的类,以便您可以存储状态。

public class PersonSet : IDbSet<Person>
{
    private readonly DbSet<Person> _dbSet;

    public PersonSet(DbSet<Person> dbSet)
    {
        _dbSet = dbSet;
    }

    public int MyProperty { get; set; }

    #region implementation of IDbSet<Person>

    public Person Add(Person entity)
    {
        return _dbSet.Add(entity);
    }

    public Person Remove(Person entity)
    {
        return _dbSet.Remove(entity);
    }

    /* etc */
    #endregion
}

然后在您的 DbContext 中,为您的自定义 DbSet 放置一个 getter:

public class MyDbContext: DbContext
{
    public DbSet<Person> People { get; set; }

    private PersonSet _personSet;
    public PersonSet PersonSet
    {
        get 
        {
            if (_personSet == null)
                _personSet = new PersonSet( Set<Person>() );

            _personSet.MyProperty = 10;

            return _personSet;
        }
        set
        {
            _personSet = value;
        }
    }

}
于 2015-04-09T03:32:53.247 回答
5

我找到了一个适合我的答案。我在上下文中将我的 DbSet 属性声明为派生接口,例如:

IDerivedDbSet<Customer> Customers { get; set; }
IDerivedDbSet<CustomerOrder> CustomerOrders { get; set; }

我的实现包括一个在构造函数中分配的私有 IDbSet,例如:

 public class DerivedDbSet<T> : IDerivedDbSet<T> where T : class
 {
    private readonly IDbSet<T> _dbSet;

    public DerivedDbSet(IDbSet<T> dbSet)
    {
        this._dbSet = dbSet;
    }
 ...
 }

我对派生 DbContext 接口的实现隐藏了 Set<>() 方法,如下所示:

new public IDerivedSet<TEntity> Set<TEntity>() where TEntity : class
    {
        //Instantiate _dbSets if required
        if (this._dbSets == null)
        {
            this._dbSets = new Dictionary<Type, object>();
        }

        //If already resolved, return stored reference
        if (this._dbSets.ContainsKey(typeof (TEntity)))
        {
            return (IDerivedSet<TEntity>) this._dbSets[typeof (TEntity)];
        }
        //Otherwise resolve, store reference and return 
        var resolvedSet = new GlqcSet<TEntity>(base.Set<TEntity>());
        this._dbSets.Add(typeof(TEntity), resolvedSet);
        return resolvedSet;
    }

派生的 DbContext 返回一个新构造的 IDerivedSet 或选择它在字典中缓存的引用。在派生的 DbContext 中,我从构造函数调用一个方法,该方法使用类型反射来遍历 DbContexts 属性并使用它自己的 Set 方法分配值/引用。看这里:

 private void AssignDerivedSets()
    {
        var properties = this.GetType().GetProperties();
        var iDerivedSets =
            properties.Where(p =>
                p.PropertyType.IsInterface &&
                p.PropertyType.IsGenericType &&
                p.PropertyType.Name.StartsWith("IDerivedSet") &&
                p.PropertyType.GetGenericArguments().Count() == 1).ToList();

        foreach (var iDerivedSet in iDerivedSets)
        {
            var entityType = iDerivedSet.PropertyType.GetGenericArguments().FirstOrDefault();
            if (entityType != null)
            {
                var genericSet = this.GetType().GetMethods().FirstOrDefault(m =>
                    m.IsGenericMethod &&
                    m.Name.StartsWith("Set") &&
                    m.GetGenericArguments().Count() == 1);
                if (genericSet != null)
                {
                    var setMethod = genericSet.MakeGenericMethod(entityType);
                    iDerivedSet.SetValue(this, setMethod.Invoke(this, null));
                }
            }
        }
    }

对我来说是一种享受。我的上下文类具有我的集合类型的可导航集合属性,它实现了继承 IDbSet 的派生接口。这意味着我可以在我的集合类型中包含查询方法,以便查询是可单元测试的,而不是使用 Queryable 类中的静态扩展。(Queryable 方法由我自己的方法直接调用)。

于 2015-10-08T15:04:19.747 回答
3

我使用另一个变量来实例化“常规”DbSet 解决了这个问题。

    private DbSet<Person> _persons { get; set; }
    public PersonDbSet<Person> Persons { get { return new PersonDbSet(_persons); } }

这样 entityframework 可以识别实体,但我仍然可以使用自己的 DbSet 类。

于 2017-05-07T19:17:46.307 回答
2

我知道这真的很老了,OP 可能已经继续了,但我自己也想知道同样的事情。EF 在运行时填充 MyContext 中的 DbSet。

我刚刚创建了继承自 DbSet<T> 的 MyDbSet<T>,并将所有对 DbSet<T> 的引用替换为 MyContext 中的派生类。运行我的程序未能实例化任何属性。

接下来我尝试将属性设置为 IDbSet<T> 因为 DbSet<T> 实现了这个接口。这确实有效。

进一步调查,DbSet 的构造函数是受保护的和内部的(受保护的构造函数无论如何都会调用内部构造函数)。所以微软很难推出你自己的版本。您可能能够通过反射访问内部构造函数,但 EF 可能无论如何都不会构造您的派生类。

我建议编写一个扩展方法来将功能插入 DbSet 对象,但是如果你想存储状态,你会被卡住。

于 2014-07-30T10:58:00.043 回答