C# 我如何通过接口解决这个问题?
我使用的是实体框架,这就是我的问题所在,但它可能并不相关,因为我认为这是一个通用的C#问题 我有一个C# 我如何通过接口解决这个问题?,c#,interface,C#,Interface,我使用的是实体框架,这就是我的问题所在,但它可能并不相关,因为我认为这是一个通用的C#问题 我有一个MyContext类,它有许多DbSet属性 为了对其进行单元测试,我编辑了MyContext,使其成为IDbSet,并对其进行了模拟IDbSet是实体框架的一部分,DbSet实现了它,因此两者几乎相同 一切都是这样工作的,因为我可以对IDbSet做我能对DbSet 除了一件事,DbSet之外的所有东西都有一个名为SqlQuery(…)的方法,我也希望能够从我的IDbSet调用该方法 由于我无法更
MyContext
类,它有许多DbSet
属性
为了对其进行单元测试,我编辑了MyContext,使其成为IDbSet
,并对其进行了模拟IDbSet
是实体框架的一部分,DbSet实现了它,因此两者几乎相同
一切都是这样工作的,因为我可以对IDbSet
做我能对DbSet
除了一件事,DbSet
之外的所有东西都有一个名为SqlQuery(…)
的方法,我也希望能够从我的IDbSet
调用该方法
由于我无法更改DbSet
或IDbSet
,这给我留下了一个难题。我不知道该怎么做,所以我的IDbSet
能够以某种逻辑方式向其添加SearchQuery(…)
方法
这有意义吗?我很困惑,所以我可能忽略了一些非常简单的事情,比如复制和粘贴
IDbSet
,重命名它并添加SearchQuery()
。有什么想法吗?嗯,这可能不是最优雅的解决方案,但您可以简单地编写如下扩展方法:
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();
}
}
public IEnumerable SearchQuery(此IDbSet集,字符串查询)
{
var dbSet=设置为dbSet;
if(dbSet!=null)
{
返回dbSet.SqlQuery(query);
}
其他的
{
抛出新的NotSupportedException();
}
}
编辑
我相信我得到了你想要的东西-到处都是包装纸,可能比它的价值更麻烦,但值得一看:
// 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]));
}
}
}
//实现IDbSet并添加到所需方法上的接口
公共接口IExtendedBSet:IDbSet
T:在哪里上课
{
DbSqlQuery SqlQuery(字符串sql,对象[]参数);
}
//通过环绕常规DbSet实现此接口。
//只需包装DbSet即可实现所有方法和属性
//召唤
公共类extendedbset:iextendedbset
T:在哪里上课
{
私有只读数据库集_DbSet;
公共扩展DbSet(DbSet-DbSet){u-DbSet=DbSet;}
DbSqlQuery iextendedbset.SqlQuery(字符串sql,对象[]参数)
{
返回_dbSet.SqlQuery(sql,参数);
}
添加(T实体){return_dbSet.Add(实体);}
T IDbSet.Attach(T entity){return_dbSet.Attach(entity);}
TDerivedEntity IDbSet.Create(){return _dbSet.Create();}
T IDbSet.Create(){return_dbSet.Create();}
T IDbSet.Find(params object[]keyValues){return_dbSet.Find(keyValues);}
ObservableCollection IDbSet.Local{get{return}
删除(T实体){return_dbSet.Remove(实体);}
IEnumerator IEnumerable.GetEnumerator(){return((IEnumerable)_dbSet.GetEnumerator();}
IEnumerator IEnumerable.GetEnumerator(){return((IEnumerable)_dbSet.GetEnumerator();}
类型IQueryable.ElementType{get{return((IQueryable)u dbSet.ElementType;}
表达式{get{return((IQueryable)u dbSet.Expression;}
提供程序{get{return((IQueryable)\ u dbSet.Provider;}}
}
//一个常规的上下文类,没有要实现的特殊接口或
//自定义属性或任何东西。
公共类MyContext:DbContext
{
公共数据库集车辆{get;set;}
}
//一个表示上下文的接口,它公开了扩展的数据库集
//为了你的布景。还可以定义SaveChanges()以及您可能需要的任何其他内容
//调用上下文对象。
公共接口IMyContext
:IDisposable
{
IExtendedbset Cars{get;}
int SaveChanges();
}
//围绕常规上下文的包装器。对于每个集合,返回一个
//扩展数据库集包装器。
公共类MyContextWrapper:IMyContext
{
私有只读MyContext\u MyContext;
公共MyContextWrapper(MyContext MyContext){u MyContext=MyContext;}
iextenddbset IMyContext.Cars
{
获取{returnnewextendedbset(_myContext.Cars);}
}
void IDisposable.Dispose()无效
{
_myContext.Dispose();
}
int IMyContext.SaveChanges()
{
返回_myContext.SaveChanges();
}
}
//将上下文变量定义为IMyContext,并创建它
//通过围绕常规上下文创建包装器。属性
//该接口的一部分将围绕您的集合进行扩展包装。
内部课程计划
{
私有静态void Main(字符串[]args)
{
使用(IMyContext context=new MyContextWrapper(new MyContext()))
{
Console.WriteLine(context.Cars.SqlQuery(“选择1”,新对象[0]);
}
}
}
一个能概括你的问题的标题怎么样?你能想出一个吗?我不能:S我尝试过“当通过更改属性来使用类本身的接口来模拟类时,当我无法访问接口或类(因为它们属于外部库)时,如何保留对不属于原始接口的方法的访问权限?”但是最大长度是150个字符。这不会使所有使用SearchQuery的测试都失败吗?我可能可以使用反射或类似的方法,而不是只强制转换到DbSet
。除非有更好的解决办法。也许我可以扩展DbSet
并在其上添加一个接口。当我第一次开始使用EF时,没有IDbSet
,所以我就是这么做的。也许我可以让它实现IDbSet
,而不是存储对DbSet的引用。在本例中,iextendedbset
已经实现了IDbSet
,因此它从基本接口获取所有其他属性/方法。您仍然需要一个常规的DbSet
实例,因为实体框架提供了这个实例。您可以尝试从DbSet
继承,而不是合成,但是您可能无法获得所有的p