简体   繁体   English

如何通过接口解决这个问题?

[英]How can I get around this issue with interfaces?

I am using Entity Framework and that's where my issue is, but it is probably not relevant as I think this is a generic C# question. 我正在使用实体框架,这就是我的问题所在,但它可能不相关,因为我认为这是一个通用的C#问题。

I have a MyContext class which has many DbSet<T> properties. 我有一个MyContext类,它有许多DbSet<T>属性。

In order to unit test it, I edited MyContext so these are IDbSet<T> instead and mock it all up. 为了对它进行单元测试,我编辑了MyContext,所以这些是IDbSet<T>而是模拟它。 IDbSet<T> is part of Entity Framework and DbSet implements it so the two are almost identical. IDbSet<T>是Entity Framework的一部分,DbSet实现它,因此两者几乎完全相同。

Everything works great like this, because I can do to IDbSet<T> everything I could do to DbSet<T> 一切都很像这样,因为我可以做IDbSet<T>我可以对DbSet<T>做的一切

Everything except for one thing - DbSet has a method called SqlQuery(..) that I also want to be able to call from my IDbSet . 除了一件事之外的一切DbSet有一个叫做SqlQuery(..)的方法,我也希望能够从我的IDbSet调用IDbSet

Since I can't alter DbSet or IDbSet , this leaves me with a conundrum. 由于我不能改变DbSetIDbSet ,这给我留下了一个难题。 I can't get my head around exactly how I can make it so my IDbSet is able to have a SearchQuery(..) method added to it in some logical fashion. 我无法理解我是如何做到这一点所以我的IDbSet能够以某种逻辑方式添加一个SearchQuery(..)方法。

Does that make sense? 那有意义吗? I'm quite confused so I may have overlooked something really simple, such as copy & pasting IDbSet and renaming it and adding SearchQuery() . 我很困惑所以我可能忽略了一些非常简单的东西,比如复制和粘贴IDbSet并重命名它并添加SearchQuery() Any ideas? 有任何想法吗?

EDIT 编辑

I believe I got what you're looking for - wrappers everywhere, possibly more trouble than it's worth, but worth a look: 我相信我得到了你想要的东西 - 到处都是包装纸,可能比它的价值更麻烦,但值得一看:

// An interface which implements IDbSet<T> and adds on the method you want
public interface IExtendedDbSet<T> : IDbSet<T>
    where T : class
{
    DbSqlQuery<T> SqlQuery(string sql, object[] parameters);
}

// Implement this interface by wrapping around a regular DbSet<T>.
// You implement all the methods and properties by just wrapping the DbSet<T>
// calls
public class ExtendedDbSet<T> : IExtendedDbSet<T>
    where T : class
{
    private readonly DbSet<T> _dbSet;

    public ExtendedDbSet(DbSet<T> dbSet) { _dbSet = dbSet; }

    DbSqlQuery<T> IExtendedDbSet<T>.SqlQuery(string sql, object[] parameters)
    {
        return _dbSet.SqlQuery(sql, parameters);
    }

    T IDbSet<T>.Add(T entity) { return _dbSet.Add(entity); }
    T IDbSet<T>.Attach(T entity) { return _dbSet.Attach(entity); }
    TDerivedEntity IDbSet<T>.Create<TDerivedEntity>() { return _dbSet.Create<TDerivedEntity>(); }
    T IDbSet<T>.Create() { return _dbSet.Create(); }
    T IDbSet<T>.Find(params object[] keyValues) { return _dbSet.Find(keyValues); }
    ObservableCollection<T> IDbSet<T>.Local { get { return _dbSet.Local; } }
    T IDbSet<T>.Remove(T entity) { return _dbSet.Remove(entity); }
    IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_dbSet).GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_dbSet).GetEnumerator(); }
    Type IQueryable.ElementType { get { return ((IQueryable)_dbSet).ElementType; } }
    Expression IQueryable.Expression { get { return ((IQueryable)_dbSet).Expression; } }
    IQueryProvider IQueryable.Provider { get { return ((IQueryable)_dbSet).Provider; } }
}

// A regular context class, no special interfaces to implement or
// custom properties or anything.
public class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }
}

// An interface representing your context, which exposes extended DbSet<T>
// for your sets. Also define SaveChanges() and whatever else you may need
// to call on your context object.
public interface IMyContext
    : IDisposable
{
    IExtendedDbSet<Car> Cars { get; }
    int SaveChanges();
}

// A wrapper around your regular context. For each set, return an
// ExtendedDbSet<T> wrapper.
public class MyContextWrapper : IMyContext
{
    private readonly MyContext _myContext;

    public MyContextWrapper(MyContext myContext) { _myContext = myContext; }

    IExtendedDbSet<Car> IMyContext.Cars
    {
        get { return new ExtendedDbSet<Car>(_myContext.Cars); }
    }

    void IDisposable.Dispose()
    {
        _myContext.Dispose();
    }

    int IMyContext.SaveChanges()
    {
        return _myContext.SaveChanges();
    }
}

// Define your context variable as IMyContext, and create it
// by creating a wrapper around a regular context. The properties
// of the interface will be extended wrappers around your sets.
internal class Program
{
    private static void Main(string[] args)
    {
        using (IMyContext context = new MyContextWrapper(new MyContext()))
        {
            Console.WriteLine(context.Cars.SqlQuery("select 1", new object[0]));
        }
    }
}

Hmm, it may not be the most elegant solution, but you could simply write an extension method like this: 嗯,它可能不是最优雅的解决方案,但您可以简单地编写这样的扩展方法:

public IEnumerable<T> SearchQuery(this IDbSet<T> set, string query)
{
    var dbSet = set as DbSet<T>;
    if (dbSet != null)
    {
        return dbSet.SqlQuery(query);
    }
    else 
    {
        throw new NotSupportedException();
    }
}

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

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