LINQ - 在C#中在运行时更改数据源(和LINQ提供程序)

时间:2012-02-17 15:04:35

标签: c# linq

我正在设计一个数据信息接口层。我希望这个层的用户不知道数据源,仍然使用LINQ语法的优点。我想使用标准LINQ提供程序作为此层的实现,并希望避免编写Custom 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实现? 或者有更好的方法来实现我在这里尝试做的事情吗? 提前谢谢。

2 个答案:

答案 0 :(得分:2)

我认为你正在寻找像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 here之间的一些区别。但基本上,IQueryable生成SQL并将其发送到数据库,而IEnumerable调用数据库然后在内存中进行查询。

这样,您可以将ORM从LINQ更改为SQL实体框架或任何可以将查询抽象为IQueryable或IEnumerable的内容。

答案 1 :(得分:1)

看起来你的IMyDataProviderLayer基本上就是你的“存储库”抽象。您可以使用该接口的实现或模拟返回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()的调用会将该列表转换为IQueryable,而不是使用上面的包装器。

很抱歉这个混乱:)