LINQ-在C#中运行时更改数据源(和LINQ提供程序)
本文关键字:LINQ 程序 数据源 运行时 LINQ- | 更新日期: 2023-09-27 18:25:09
我正在设计一个数据信息接口层。我希望这个层的用户不知道数据源,仍然使用LINQ语法的优点。我想使用标准LINQ提供程序作为该层的实现,并希望避免编写自定义LINQ提供器
考虑以下示例。
信息接口层声明如下
interface IMyData
{
int Intdata { get; }
double DoubleData { get; }
}
interface IMyDataProviderLayer
{
IQueryable< IMyData > Context { get; }
}
以及在使用该层的客户端代码中
//dataProvider implements IMyDataProviderLayer
var dataCollection = from data in dataProvider.Context
where data.Intdata == 5
select data;
接口实现将访问来自真实数据源的数据,并且需要使用标准LINQ提供程序。
有可能做上面这样的事情吗?即使数据源有标准的LINQ实现,我是否需要从头开始实现LINQ提供程序?或者有更好的方法来实现我在这里想要做的事情吗?提前谢谢。
我认为您正在寻找类似Repository Pattern的东西。你会有一个如下的界面:
public interface IMyRepository
{
IEnumberable<MyObject> GetObjects();
}
实现(此处使用依赖项注入):
public class MyRepository : IMyRepository
{
private Context dbContext;
IEnumberable<MyObject> GetObjects()
{
return dbContext.MyObjects;
}
}
用途:
var dataCollection = from data in repository.GetObjects()
where data.Intdata == 5
select data;
您也可以在存储库中使用IQueryable
而不是IEnumerable
。我在这里解释了2之间的一些区别。但基本上,IQueryable会生成SQL并将其发送到数据库,而IEnumerable则会调用数据库,然后在内存中进行查询。
通过这种方式,您可以将ORM从LINQ改为SQL到实体框架,或者任何可以将查询抽象为IQueryable或IEnumerable的东西。
看起来您的IMyDataProviderLayer
基本上就是您的"Repository"抽象。您可以有一个接口的实现或Mock,它返回一个支持List<T>
的IQueryable
,而不是使用访问数据库的普通提供程序。
您可以通过插入一个自定义的IQueryable
实现来实现这一点,该实现封装了IList
:
public class QueryableList<T> : System.Linq.IQueryable<T>
{
private IList<T> _data;
public QueryableList()
{
_data = new List<T>();
}
public QueryableList(IList<T> data)
{
_data = data;
}
public IEnumerator<T> GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
public Expression Expression
{
get { return Expression.Constant(_data.AsQueryable()); }
}
public IQueryProvider Provider
{
get { return _data.AsQueryable().Provider; }
}
public Type ElementType
{
get { return typeof(T); }
}
}
然后,您可以制作一个模拟IMyDataProviderLayer
,返回其中一个作为其Context
。例如(使用Moq):
// Make a mock data access layer, that is backed by a List.
var mockedDataLayer = new Mock<IMyDataProviderLayer>();
mockedDataLayer.SetupGet(x => x.Context, new QueryableList<IMyData>()
{
new MyData() { Intdata = 5, DoubleData = 1.2 },
new MyData() { Intdata = 2, DoubleData = 6.8 },
});
// Now we can use this.
var dataCollection = from data in mockedDataLayer.Object.Context
where data.Intdata == 5
select data;
编辑:
写完后不久,我又回到了这个问题上,意识到我制作了IQueryable列表包装器来解决另一个问题。实际上,你可以完全忽略这一点,只需执行:
using System.Linq;
IQueryable<IMyData> mydata = new List<IMyData>()
{
new MyData() { Intdata = 5, DoubleData = 1.2 },
new MyData() { Intdata = 2, DoubleData = 6.8 }
}.AsQueryable();
对.AsQueryable()
的调用将该List转换为IQueryable,而不是使用上面的包装器。
抱歉造成混淆:)