继承自DbSet <t>以添加属性</t>

时间:2013-12-06 15:42:37

标签: entity-framework dbcontext inheritance dbset

有没有办法从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 个答案:

答案 0 :(得分: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;
        }
    }

}

答案 1 :(得分:4)

我找到了一个适合我的答案。我在我的上下文中将我的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&lt;&gt;()方法,如下所示:

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,或者选择它在Dictionary中缓存的引用。在派生的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));
                }
            }
        }
    }

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

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:1)

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

我刚刚创建了MyDbSet&lt; T&gt;继承自DbSet&lt; T&gt;并且替换了对DbSet&lt; T&gt;的所有引用。在MyContext中使用我的派生类。运行我的程序无法实例化任何属性。

接下来,我尝试将属性设置为IDbSet&lt; T&gt;因为DbSet&lt; T&gt;实现这个接口。这可行。

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

我建议编写一个扩展方法将功能插入到DbSet对象中,但是如果你想存储状态,你就会陷入困境。