Amol Gawai
Amol Gawai

Reputation: 3350

LINQ - changing Data Source (and the LINQ Provider) at runtime in C#

I am designing a Data Information Interface Layer. I want the users of this layer to be not aware of the Data Source and still use the goodness of LINQ syntax. I want to use standard LINQ providers as implementation of this layer and want to avoid writing Custom LINQ provider

Consider following Example.

The Information Interface Layer is declared as following

interface IMyData
{
    int Intdata { get; }
    double DoubleData { get; }
}


interface IMyDataProviderLayer
{
    IQueryable< IMyData > Context { get; }
}

and in Client code that uses this layer

    //dataProvider implements IMyDataProviderLayer
var dataCollection = from data in dataProvider.Context
                                 where data.Intdata == 5
                                 select data;

The interface implementation will access the data from real data source and needs to use the standard LINQ providers.

Is it possible to do something like above? Do I need to implement LINQ provider from scratch even the Data Source has standard LINQ implementations? Or Is there a better way to achieve what I am trying to do here? Thanks in advance.

Upvotes: 1

Views: 959

Answers (2)

CodingWithSpike
CodingWithSpike

Reputation: 43728

It looks like your IMyDataProviderLayer is basically your "Repository" abstraction. You can have an implementation or a Mock of that interface that returns a List<T> backed IQueryable instead of using the normal provider that hits the database.

You can achieve this by plugging in a custom IQueryable implementation that wrappers an 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); }
    }
}

Then you can make a mock IMyDataProviderLayer that returns one of these as its Context. For example (using 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;

Edit:

I came back to this a little while after I wrote it, and realized that I had made that IQueryable list wrapper to solve a different problem. You cna actually completely omit that, and simply do:

using System.Linq;

IQueryable<IMyData> mydata = new List<IMyData>()
    {
        new MyData() { Intdata = 5, DoubleData = 1.2 },
        new MyData() { Intdata = 2, DoubleData = 6.8 }
    }.AsQueryable();

The call to .AsQueryable() turns that List into an IQueryable for you, instead of using my wrapper above.

Sorry for the confusion :)

Upvotes: 1

Shawn Mclean
Shawn Mclean

Reputation: 57469

I think you are looking for something like a Repository Pattern. You would have an interface as follows:

public interface IMyRepository
{
    IEnumberable<MyObject> GetObjects();
}

Implementation(use dependency injection here):

public class MyRepository : IMyRepository
{
    private Context dbContext;

    IEnumberable<MyObject> GetObjects()
    {
         return dbContext.MyObjects;
    }
}

Use:

var dataCollection = from data in repository.GetObjects()
                             where data.Intdata == 5
                             select data;

You may also use IQueryable instead of IEnumerable in you repository. I explained some difference between the 2 here. But basically, the IQueryable has the SQL get generated and sent to the database while IEnumerable calls the database then does the query in memory.

This way, you may change out the ORM from LINQ to SQL to entity framework or anything that can abstract the query to IQueryable or IEnumerable.

Upvotes: 2

Related Questions