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提供程序?或者有更好的方法来实现我在这里想要做的事情吗?提前谢谢。

LINQ-在C#中运行时更改数据源(和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,而不是使用上面的包装器。

抱歉造成混淆:)