Skip to content

Data Access

YGET initial architecture explanation

My current position is with Corefount Inc. as a Principal Architect and we are putting together a mobile platform for preventive healthcare. To accomplish this goal there are a few requirements:

  • Mobile Application UI
  • Web Application UI
  • Data Driven Backend

There are an unlimited number of ways to approach developing these items. I will layout what choices we have currently begun implementation with and why we chose them. One of the primary driving factors behind the decisions are the existing staff’s skill set. Myself, My Boss, and the QA lead are all experienced with Microsoft technologies. Also, we do not have any infrastructure and consider the overhead of maintaining our own servers an unnecessary burden at this stage. Another leading factor in the decision making is the initial teams Xamarin experience. Now that I have explained our backgrounds, I will expand on the initial requirements.

Mobile Application


The mobile application allows users to track their daily nutritional information while also prompting them with what they are lacking. It will also help them avoid items that they are allergic to or have a general disinterest in consuming. The application needs to allow the user to login using corporate credentials and may need to be hosted from a corporate app store. Users should be able to use the application offline. The application should support Android, iOS, and Windows Phone. This lead to the following decisions being made:

  • Xamarin Forms
    • Xamarin Forms allows us to build a cross platform UI with Xaml and easily override needs per platform.
  • sqlite
    • sqlite makes caching data locally easy.
    • Xamarin has an easy to use cross platform plugin for sqlite
  • Web API 2 client
    • Web API 2 client works with all the mobile platforms as a PCL
    • The backend is powered by Web API (this will be discussed later)
    • The identity provider is also through ASP.NET and can utilize the web api client
  • Syncfusion
    • Has controls with Xamarin Forms that allow us to quickly put together dashboards for users to track their progress

 

Web Application


The web application has many of the same requirements as the mobile application. The only true difference is the web application doesn’t need internal hosting or offline usage and it needs to be responsive. This lead to the following decisions to be made:

  • angularjs
    • allows for creating a SPA and seems to be the easiest to hire maintainers for
    • allows for reuse of existing JQuery widgets as directives
    • allows for code reuse between data management portals and consumer facing application
  • typescript
    • typescript gives us type checking and intellisense for writing large amounts of javascript
    • seems to be an easier pickup for non javascript developers (sometimes you have to throw bodies at a problem)
    • used with definitely typed gives intellisense and compile time checking to existing libraries
  • bootstrap
    • can quickly create a responsive website
    • customizable via less or sass compiler
  • less
    • helps to create complex css
    • extends bootstrap 3
  • kendo ui
    • facilitates the creation of dashboards quickly
    • works with angularjs
  • npm + gulp
    • manages javascript packages
    • allows for build automation and integrates easily with VS 2015

 

Data Driven Backend


The backend is going to be broken up into multiple sections. As with most platforms, the data is the real key to success. It is also the source of much of the complexity. For the YGET platform, the data drives decisions that affect patient’s lives and has to be good enough not to cause them harm. Because of this, the data has to be properly vetted and organized for use. Along with that, there should be multiple redundancies to ensure patients are not harmed.

There are some encompassing technological choices that were made that can be discussed in this section. One of the major choices was to use Microsoft Azure. Azure allowed us to host our solution more easily than managing our own data centers or managing more than one data center vendor. Azure was also chosen over other cloud providers due to the team’s knowledge of the product and our relationship with the local Microsoft team (I can’t state enough how important having local contacts are). Also, SQL Server was chosen as our primary relational database engine. Corefount’s CTO (my boss) has more experience with SQL server than I have experience so it was an easy decision.

Now that the over arching decisions have been explained, its time to look at the systems required to power the backend of the platform. I will try to organize the following systems in least dependent to most dependent:

 

Data Generation and Acquisition


YGET‘s need for data will require lots of manual input and lots of automated processes. First, let us focus on the automated processes. The major process currently is targeted web crawlers parsing aggregate data feeds. There are also smaller processes that grab published data from the web for import (pdfs and the like). These processes are run in Azure’s Web Jobs due to the simplicity of scheduling and the ability to expand resources on demand.

The web crawler needed to be a very low overhead and fast worker. This caused us to decide to write it in Visual C++. This lead to the following decisions:

  • boost
    • full of helper functions and classes (especially in string searching)
  • Casablanca
    • utilizes PPL
    • supports json and makes http lifecycle easier
  • sql api
    • simplest SQL Server client library I could find

After the web crawler has parsed and saved the data successfully there is an SSIS package that is able to take the data and put it into the management database.

 

Data Management and Configuration


After the data is acquired, it needs to be organized and sanitized. Also, the data may be incomplete and manual input may be needed. To accomplish this an internal website was put together using the following:

  • ASP.NET Web API
    • Easily expose OData endpoints for use with kendo ui tools
    • Expose data quickly and easily
  • angularjs
    • integrates with kendo ui easily
    • allows for SPA application
    • internationalization support for internationalization market data manipulation
  • kendo ui
    • controls allow for easy data management
    • controls support out of the box paging and filtering with odata
    • internationalization ease for international market data manipulation
  • Entity Framework
    • makes data access a non issue
    • works in the simplest manner possible with ASP.NET Web API OData

Data Endpoints


Finally, the last piece of the puzzle is exposing the data to the end user. The data will need to be available to users globally. The data also needs to be secured so that user’s can access only their data. User’s may be apart of a corporate domain, use a social login, or use a YGET local account. These requirements led to the following decisions:

  • ASP.NET Web API
    • Easily expose http endpoints
  • ASP.NET Identity
    • allows for integration with third party OAuth services
    • allows for integration with on premises and azure active directory
    • supports local accounts
  • AutoMapper
    • Allows for easy exposing of DTOs
  • Azure Service Bus
    • allows for offloading of computationally heavy code
  • Entity Framework
    • makes data access a non issue
  • Sql Azure
    • relational database hosted instance that covers data needs in the cloud

Generic Repository for DbContext and ODataContext

At my current client there was a need for being able to change data sources from databases to OData feeds. Since dependency injection is being used for dependencies, the data source is going to be injected into the object that needs it. To utilize dependency injection to solve the problem of being able to easily swap the data source, a generic interface needs to defined to which both a Entity Framework client and an OData client can implement. The generic repository is built from a set of interfaces.

IReadOnlyRepository:


/// <summary>
/// Interface IReadOnlyRepository
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public interface IReadOnlyRepository<EntityType> : IDisposable where EntityType : class
{        /// <summary>
    /// Gets the underlying queryable.
    /// </summary>
    /// <value>The underlying queryable.</value>
    IQueryable<EntityType> Queryable { get; }

    /// <summary>
    /// Gets the local entities.
    /// </summary>
    /// <value>The local entities.</value>
    IEnumerable<EntityType> Local { get; }

    /// <summary>
    /// Get by the specified query function
    /// </summary>
    /// <param name="queryFunc"></param>
    /// <returns>IQueryable&lt;EntityType&gt;.</returns>
    Task<IEnumerable<EntityType>> Get(Func<IQueryable, IQueryable> queryFunc,CancellationToken cancellationToken);

    /// <summary>
    /// Gets by the specified filters.
    /// </summary>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>IEnumerable&lt;EntityType&gt;.</returns>
    IEnumerable<EntityType> Get(Expression<Func<EntityType, bool>> filter = null, Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,IncludeWrapper<EntityType> includeWrapper = null);

    /// <summary>
    /// Gets by the specified filters asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>Task&lt;IEnumerable&lt;EntityType&gt;&gt;.</returns>
    Task<IEnumerable<EntityType>> GetAsync(CancellationToken cancellationToken, Expression<Func<EntityType, bool>> filter = null, Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,IncludeWrapper<EntityType> includeWrapper = null);

    /// <summary>
    /// Get by the specified query function async
    /// </summary>
    /// <param name="queryFunc"></param>
    /// <returns>IQueryable&lt;EntityType&gt;.</returns>
    Task<IQueryable<EntityType>> GetAsync(CancellationToken cancellationToken, Func<IQueryable, IQueryable> queryFunc);

    /// <summary>
    /// Get by the specified query function
    /// </summary>
    /// <param name="queryFunc"></param>
    /// <returns>IQueryable&lt;TDto e&gt;.</returns>
    IQueryable<TDataObject> Get<TDataObject>(Func<IQueryable, IQueryable<TDataObject>> queryFunc);

    /// <summary>
    /// Gets the specified select statement.
    /// </summary>
    /// <typeparam name="T">The type to select out of the query</typeparam>
    /// <param name="selectStatement">The select statement.</param>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>IEnumerable&lt;T&gt;.</returns>
    IEnumerable<T> Get<T>(Expression<Func<EntityType, T>> selectStatement, Expression<Func<EntityType, bool>> filter = null, Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null, IncludeWrapper<EntityType> includeWrapper = null);

    /// <summary>
    /// Gets the specified select statement asynchronous.
    /// </summary>
    /// <typeparam name="T">the type to select out of the query</typeparam>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="selectStatement">The select statement.</param>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>Task&lt;IEnumerable&lt;T&gt;&gt;.</returns>
    Task<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken, Expression<Func<EntityType, T>> selectStatement, Expression<Func<EntityType, bool>> filter = null, Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,IncludeWrapper<EntityType> includeWrapper = null);

    Task<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken, IQueryable<T> query);

    /// <summary>
    /// Gets the entity by identifier.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <returns>EntityType.</returns>
    EntityType GetById(object id);

    /// <summary>
    /// Gets the entity by identifier asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="id">The identifier.</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    Task<EntityType> GetByIdAsync(CancellationToken cancellationToken, object id);

    /// <summary>
    /// Gets a single entity.
    /// </summary>
    /// <param name="filter">The filter.</param>
    /// <returns>EntityType.</returns>
    EntityType GetSingle(Expression<Func<EntityType, bool>> filter);

    /// <summary>
    /// Gets a single entity asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="filter">The filter.</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    Task<EntityType> GetSingleAsync(CancellationToken cancellationToken, Expression<Func<EntityType, bool>> filter);

    void Load<TRelatedEntity>(EntityType entity, Expression<Func<EntityType, TRelatedEntity>> relation) where TRelatedEntity : class;
    void Load<TRelatedEntity>(EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation) where TRelatedEntity : class;
    Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, EntityType entity, Expression<Func<EntityType, TRelatedEntity>> relation) where TRelatedEntity : class;
    Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation) where TRelatedEntity : class;

    void Load<TRelatedEntity>(EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation, Expression<Func<TRelatedEntity, bool>> filter) where TRelatedEntity : class;
    Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation, Expression<Func<TRelatedEntity, bool>> filter) where TRelatedEntity : class;

}

ISaveable:

public interface ISaveable
{
    /// <summary>
    /// Saves this instance.
    /// </summary>
    void Save();

    /// <summary>
    /// Saves the added entities asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>Task.</returns>
    Task SaveAsync(CancellationToken cancellationToken);

}

IUpdateableRepository:


/// <summary>
/// Interface IUpdateableRepository
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public interface IUpdateableRepository<EntityType> : ISaveable, ISupportUndo, IDisposable where EntityType : class
{
    /// <summary>
    /// Updates the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>EntityType.</returns>
    EntityType Update(EntityType entity, bool saveImmediately = false);

    /// <summary>
    /// Updates the entity asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    Task<EntityType> UpdateAsync(CancellationToken cancellationToken, EntityType entity, bool saveImmediately = false);

    /// <summary>
    /// Updates the entity identified by the specified identifier.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <param name="updateAction">The update action.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>EntityType.</returns>
    EntityType Update(object id, Action<EntityType> updateAction, bool saveImmediately = false);

    /// <summary>
    /// Updates the entity identified by the specified identifier asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="id">The identifier.</param>
    /// <param name="updateAction">The update action.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    Task<EntityType> UpdateAsync(CancellationToken cancellationToken, object id, Action<EntityType> updateAction, bool saveImmediately = false);
}

ICreateableRepository:


/// <summary>
/// Interface ICreateableRepository
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public interface ICreateableRepository<EntityType> : ISaveable, IDisposable where EntityType : class
{
    /// <summary>
    /// Creates the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <returns>EntityType.</returns>
    EntityType Create(EntityType entity, bool saveImmediately = false);

    /// <summary>
    /// Creates the entity asynchronous.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    Task<EntityType> CreateAsync(EntityType entity, CancellationToken cancellationToken, bool saveImmediately = false);

}

IDeleteableRepository:


/// <summary>
/// Interface IDeleteableRepository
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public interface IDeleteableRepository<EntityType> : ISaveable, IDisposable where EntityType : class
{
    /// <summary>
    /// Deletes the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    void Delete(EntityType entity, bool saveImmediately = false);

    /// <summary>
    /// Deletes the entity asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task.</returns>
    Task DeleteAsync(CancellationToken cancellationToken, EntityType entity, bool saveImmediately = false);

    /// <summary>
    /// Deletes the entity identified specified identifier.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    void Delete(object id, bool saveImmediately = false);

    /// <summary>
    /// Deletes the entity asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="id">The identifier.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task.</returns>
    Task DeleteAsync(CancellationToken cancellationToken, object id, bool saveImmediately = false);
}

IWriteOnlyRepository:


/// <summary>
/// Interface IWriteOnlyRepository
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public interface IWriteOnlyRepository<EntityType>
    : ICreateableRepository<EntityType>,
    IDeleteableRepository<EntityType>,
    IUpdateableRepository<EntityType>, ISaveable, ISupportUndo where EntityType : class
{

}

Finally IGenericRepository:


/// <summary>
/// Interface IGenericRepository
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public interface IGenericRepository<EntityType> : CRUD.IReadOnlyRepository<EntityType>,
    CRUD.IWriteOnlyRepository<EntityType>, CRUD.ISaveable, CRUD.ISupportUndo where EntityType : class
{
}

Now that the interface is defined, we need an implementation for each interface. Due to its maturity, the Entity Framework implementation is a bit more straight forward. Lets take a look at it first.


/// <summary>
/// Class GenericRepository.
/// </summary>
/// <typeparam name="EntityType">The type of the entity type.</typeparam>
public class GenericRepository<EntityType> : Repository.IGenericRepository<EntityType>
    where EntityType : class
{
    /// <summary>
    /// The _DB set
    /// </summary>
    protected DbSet<EntityType> _dbSet;
    /// <summary>
    /// The _DB context
    /// </summary>
    protected DbContext _dbContext;

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository{EntityType}"/> class.
    /// </summary>
    /// <param name="dbContext">The database context.</param>
    public GenericRepository(DbContext dbContext)
    {
        _dbContext = dbContext;
#if DEBUG
        dbContext.Database.Log = x => System.Diagnostics.Trace.WriteLine(x);
#endif
        _dbSet = _dbContext.Set<EntityType>();
    }

    #region IGenericRepository Implementation

    /// <summary>
    /// Gets the queryable.
    /// </summary>
    /// <value>The queryable.</value>
    public virtual IQueryable<EntityType> Queryable
    {
        get { return _dbSet; }
    }

    /// <summary>
    /// Gets the specified filter.
    /// </summary>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>IEnumerable&lt;EntityType&gt;.</returns>
    public virtual IEnumerable<EntityType> Get(Expression<Func<EntityType, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,
        IncludeWrapper<EntityType> includeWrapper = null)
    {
        return CommonQuerySetup(filter, ref page, orderByWrapper,includeWrapper);
    }

    /// <summary>
    /// get as an asynchronous operation.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>Task&lt;IEnumerable&lt;EntityType&gt;&gt;.</returns>
    public virtual async Task<IEnumerable<EntityType>> GetAsync(CancellationToken cancellationToken,
        Expression<Func<EntityType, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,
        IncludeWrapper<EntityType> includeWrapper = null)
    {
        IQueryable<EntityType> queryable = CommonQuerySetup(filter, ref page, orderByWrapper,includeWrapper);
        return await queryable.ToListAsync(cancellationToken);
    }

    public virtual async Task<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken, IQueryable<T> query)
    {
        return await query.ToArrayAsync();
    }

    /// <summary>
    /// Gets the specified select statement.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="selectStatement">The select statement.</param>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>IEnumerable&lt;T&gt;.</returns>
    public virtual IEnumerable<T> Get<T>(Expression<Func<EntityType, T>> selectStatement,
        Expression<Func<EntityType, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,
        IncludeWrapper<EntityType> includeWrapper = null)
    {
        return CommonQuerySetup(filter, ref page, orderByWrapper,includeWrapper).Select(selectStatement);
    }

    /// <summary>
    /// get as an asynchronous operation.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="selectStatement">The select statement.</param>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeWrapper">The include wrapper.</param>
    /// <returns>Task&lt;IEnumerable&lt;T&gt;&gt;.</returns>
    public virtual async Task<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken,
        Expression<Func<EntityType, T>> selectStatement,
        Expression<Func<EntityType, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<EntityType> orderByWrapper = null,
        IncludeWrapper<EntityType> includeWrapper = null)
    {
        IQueryable<EntityType> queryable = CommonQuerySetup(filter, ref page, orderByWrapper,includeWrapper);
        var selectQuery = queryable.Select(selectStatement);
        return await selectQuery.ToListAsync(cancellationToken);
    }

    /// <summary>
    /// Gets the by identifier.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <returns>EntityType.</returns>
    public virtual EntityType GetById(object id)
    {
        return _dbSet.Find(id);
    }

    /// <summary>
    /// Gets the by identifier asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="id">The identifier.</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    public virtual Task<EntityType> GetByIdAsync(CancellationToken cancellationToken, object id)
    {
        return _dbSet.FindAsync(cancellationToken, id);
    }

    /// <summary>
    /// Gets the single.
    /// </summary>
    /// <param name="filter">The filter.</param>
    /// <returns>EntityType.</returns>
    public virtual EntityType GetSingle(Expression<Func<EntityType, bool>> filter)
    {
        return _dbSet.Single(filter);
    }

    /// <summary>
    /// Gets the single asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="filter">The filter.</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    public virtual Task<EntityType> GetSingleAsync(CancellationToken cancellationToken, Expression<Func<EntityType, bool>> filter)
    {
        return _dbSet.SingleAsync(filter, cancellationToken);
    }

    /// <summary>
    /// Updates the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>EntityType.</returns>
    public virtual EntityType Update(EntityType entity, bool saveImmediately = false)
    {
        _dbContext.Entry(entity).State = EntityState.Modified;
        if (saveImmediately)
        {
            _dbContext.SaveChanges();
        }
        return entity;
    }

    /// <summary>
    /// update as an asynchronous operation.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    public virtual async Task<EntityType> UpdateAsync(CancellationToken cancellationToken, EntityType entity, bool saveImmediately = false)
    {
        _dbContext.Entry(entity).State = EntityState.Modified;
        if (saveImmediately)
        {
            await _dbContext.SaveChangesAsync(cancellationToken);
        }
        return entity;
    }

    /// <summary>
    /// Updates the specified identifier.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <param name="updateAction">The update action.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>EntityType.</returns>
    public virtual EntityType Update(object id, Action<EntityType> updateAction, bool saveImmediately = false)
    {
        EntityType item = _dbSet.Find(id);
        updateAction(item);
        _dbContext.Entry(item).State = EntityState.Modified;
        if (saveImmediately)
        {
            _dbContext.SaveChanges();
        }
        return item;
    }

    /// <summary>
    /// update as an asynchronous operation.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="id">The identifier.</param>
    /// <param name="updateAction">The update action.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    public virtual async Task<EntityType> UpdateAsync(CancellationToken cancellationToken, object id, Action<EntityType> updateAction, bool saveImmediately = false)
    {
        EntityType item = await _dbSet.FindAsync(cancellationToken, id);
        updateAction(item);
        _dbContext.Entry(item).State = EntityState.Modified;
        if (saveImmediately)
        {
            await _dbContext.SaveChangesAsync(cancellationToken);
        }
        return item;
    }

    /// <summary>
    /// Creates the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <returns>EntityType.</returns>
    public virtual EntityType Create(EntityType entity, bool saveImmediately = false)
    {
        _dbSet.Add(entity);
        if (saveImmediately)
        {
            _dbContext.SaveChanges();
            _dbContext.Entry(entity).GetDatabaseValues();
        }

        return entity;
    }

    /// <summary>
    /// create as an asynchronous operation.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>Task&lt;EntityType&gt;.</returns>
    public virtual async Task<EntityType> CreateAsync(EntityType entity, CancellationToken cancellationToken, bool saveImmediately = false)
    {
        _dbSet.Add(entity);
        if (saveImmediately)
        {
            await _dbContext.SaveChangesAsync(cancellationToken);
            await _dbContext.Entry(entity).GetDatabaseValuesAsync(cancellationToken);
        }
        return entity;
    }

    /// <summary>
    /// Deletes the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    public virtual void Delete(EntityType entity, bool saveImmediately = false)
    {
        _dbSet.Remove(entity);
        if (saveImmediately)
        {
            _dbContext.SaveChanges();
        }
    }

    /// <summary>
    /// delete as an asynchronous operation.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="entity">The entity.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task.</returns>
    public virtual async Task DeleteAsync(CancellationToken cancellationToken, EntityType entity, bool saveImmediately = false)
    {
        _dbSet.Remove(entity);
        if (saveImmediately)
        {
            await _dbContext.SaveChangesAsync(cancellationToken);
        }
    }

    /// <summary>
    /// Deletes the specified identifier.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    public virtual void Delete(object id, bool saveImmediately = false)
    {
        EntityType item = _dbSet.Find(id);
        _dbSet.Remove(item);
        if (saveImmediately)
        {
            _dbContext.SaveChanges();
        }
    }

    /// <summary>
    /// delete as an asynchronous operation.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <param name="id">The identifier.</param>
    /// <param name="saveImmediately">if set to <c>true</c> [save immediately].</param>
    /// <returns>Task.</returns>
    public virtual async Task DeleteAsync(CancellationToken cancellationToken, object id, bool saveImmediately = false)
    {
        EntityType item = await _dbSet.FindAsync(cancellationToken, id);
        _dbSet.Remove(item);
        if (saveImmediately)
        {
            await _dbContext.SaveChangesAsync(cancellationToken);
        }
    }

    #endregion

    #region IDispose Implementation

    /// <summary>
    /// The _disposed
    /// </summary>
    private bool _disposed = false;
    /// <summary>
    /// Releases unmanaged and - optionally - managed resources.
    /// </summary>
    /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
    protected virtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                _dbContext.Dispose();
            }
        }
        this._disposed = true;
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    #endregion

    /// <summary>
    /// Commons the query setup.
    /// </summary>
    /// <param name="filter">The filter.</param>
    /// <param name="page">The page.</param>
    /// <param name="orderByWrapper">The order by wrapper.</param>
    /// <param name="includeByWrapper">The include by wrapper.</param>
    /// <returns>IQueryable&lt;EntityType&gt;.</returns>
    /// <exception cref="System.NotSupportedException">Paging not supported without an order by clause</exception>
    private IQueryable<EntityType> CommonQuerySetup(Expression<Func<EntityType, bool>> filter, ref Page page, OrderByWrapper<EntityType> orderByWrapper, IncludeWrapper<EntityType> includeByWrapper)
    {
        IQueryable<EntityType> queryable = _dbSet.AsQueryable();
        if (filter != null)
        {
            queryable = queryable.Where(filter);
        }
        if (orderByWrapper != null)
        {
            foreach (var orderby in orderByWrapper.OrderByClauses)
            {
                queryable = orderby(queryable);
            }
        }
        if (page != default(Page))
        {
            if (orderByWrapper == null)
            {
                throw new NotSupportedException("Paging not supported without an order by clause");
            }
            else
            {
                queryable = queryable.Skip((page.PageNumber - 1) * page.PageSize);
                queryable = queryable.Take(page.PageSize);
            }
        }
        if (includeByWrapper != null)
        {
            if (includeByWrapper.IncludeExpressions != null)
            {
                foreach(var expression in includeByWrapper.IncludeExpressions)
                {
                    queryable = queryable.Include(expression);
                }
            }
            else if(includeByWrapper.IncludePaths != null)
            {
                foreach(var path in includeByWrapper.IncludePaths)
                {
                    queryable = queryable.Include(path);
                }
            }
        }
        return queryable;
    }

    /// <summary>
    /// Gets the local.
    /// </summary>
    /// <value>The local.</value>
    public virtual IEnumerable<EntityType> Local
    {
        get
        {
            return _dbSet.Local;
        }
    }

    /// <summary>
    /// Saves this instance.
    /// </summary>
    public virtual void Save()
    {
        _dbContext.SaveChanges();
    }

    /// <summary>
    /// Saves the asynchronous.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>Task.</returns>
    public virtual Task SaveAsync(CancellationToken cancellationToken)
    {
        return _dbContext.SaveChangesAsync(cancellationToken);
    }

    public virtual void Undo<TEntity>(TEntity entity) where TEntity :class
    {

        var entry = _dbContext.Entry(entity);
        if( entry.State == EntityState.Added )
        {
            ((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
        }
        else if (entry.State != EntityState.Unchanged && entry.State != EntityState.Detached)
        {
            entry.Reload();
        }
    }
    public virtual async Task UndoAsync<TEntity>(CancellationToken cancellationToken, TEntity entity) where TEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Added)
        {
            ((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
        }
        else if (entry.State != EntityState.Unchanged && entry.State != EntityState.Detached)
        {
            await entry.ReloadAsync(cancellationToken);
        }
    }

    public virtual void UndoAll()
    {
        foreach (var entity in _dbContext.ChangeTracker.Entries())
        {
            this.Undo(entity);
        }
    }
    public virtual async Task UndoAllAsync(CancellationToken cancellationToken)
    {
        foreach (var entity in _dbContext.ChangeTracker.Entries())
        {
            await this.UndoAsync(cancellationToken, entity);
        }
    }

    public virtual Task<IQueryable<EntityType>> GetAsync(CancellationToken cancellationToken, Func<IQueryable, IQueryable> queryFunc)
    {
        return Task.FromResult(Get(queryFunc));
    }

    public virtual async Task<IEnumerable<EntityType>> Get(Func<IQueryable, IQueryable> queryFunc, CancellationToken cancellationToken)
    {
        return (await queryFunc(this.Queryable).ToListAsync(cancellationToken)) as IEnumerable<EntityType>;
    }

    public virtual IQueryable<TDataObject> Get<TDataObject>(Func<IQueryable, IQueryable<TDataObject>> queryFunc)
    {
        return queryFunc(this.Queryable) as IQueryable<TDataObject>;
    }

    public virtual void Load<TRelatedEntity>(EntityType entity, Expression<Func<EntityType,TRelatedEntity>> relation) where TRelatedEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged)
        {
            var property = entry.Reference<TRelatedEntity>(relation);
            if (!property.IsLoaded)
                property.Load();
        }
    }
    public virtual void Load<TRelatedEntity>(EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation) where TRelatedEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged)
        {
            var collection = entry.Collection<TRelatedEntity>(relation);
            if (!collection.IsLoaded)
                collection.Load();
        }
    }
    public virtual async Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, EntityType entity, Expression<Func<EntityType, TRelatedEntity>> relation) where TRelatedEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged)
        {
            var property = entry.Reference<TRelatedEntity>(relation);
            if (!property.IsLoaded)
                await property.LoadAsync(cancellationToken);
        }
    }
    public virtual async Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation) where TRelatedEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged)
        {
            var collection = entry.Collection<TRelatedEntity>(relation);
            if (!collection.IsLoaded)
                await collection.LoadAsync(cancellationToken);
        }

    }

    public virtual void Load<TRelatedEntity>(EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation, Expression<Func<TRelatedEntity, bool>> filter) where TRelatedEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged)
        {
            entry.Collection<TRelatedEntity>(relation)
                    .Query()
                    .Where(filter)
                    .Load();
        }
    }
    public virtual Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, EntityType entity, Expression<Func<EntityType, ICollection<TRelatedEntity>>> relation, Expression<Func<TRelatedEntity, bool>> filter) where TRelatedEntity : class
    {
        var entry = _dbContext.Entry(entity);
        if (entry.State == EntityState.Modified || entry.State == EntityState.Unchanged)
        {
            return entry.Collection<TRelatedEntity>(relation)
                        .Query()
                        .Where(filter)
                        .LoadAsync(cancellationToken);
        }

        return Task.FromResult<object>(null);
    }
}

A little less mature and a little more convoluted is the WCF OData client. This makes the Data implementation a bit more difficult.


public class GenericRepository<TEntity> : Repository.IGenericRepository<TEntity> where TEntity : class
{
    private static ConcurrentDictionary<Type, Func<DataServiceContext, DataServiceQuery>> _cachedPlans = new ConcurrentDictionary<Type,Func<DataServiceContext,DataServiceQuery>>();

    protected DataServiceContext _context;
    protected DataServiceQuery<TEntity> _set;

    public GenericRepository(DataServiceContext context)
    {
        _context = context;
#if DEBUG
        _context.SendingRequest2 += _context_SendingRequest2;
        _context.ReceivingResponse += _context_ReceivingResponse;
#endif
        if (!_cachedPlans.ContainsKey(typeof(TEntity)))
        {
            AddCachedQueryPropertyPlan();
        }
        _set = _cachedPlans[typeof(TEntity)](_context) as DataServiceQuery<TEntity>;
    }

    public IQueryable<TEntity> Queryable
    {
        get { return _set; }
    }

    public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<TEntity> orderByWrapper = null,
        IncludeWrapper<TEntity> includeWrapper = null)
    {
        try
        {
            return new DataServiceCollection<TEntity>(CommonQuerySetup(filter, ref page, orderByWrapper, includeWrapper));
        }
        catch (DataServiceQueryException dsqEx)
        {
            throw new ArgumentException(dsqEx.Response.Error.Message);
        }
    }

    public Task<IEnumerable<TEntity>> GetAsync(CancellationToken cancellationToken,
        Expression<Func<TEntity, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<TEntity> orderByWrapper = null,
        IncludeWrapper<TEntity> includeWrapper = null)
    {
        return Task.FromResult(Get(filter, page, orderByWrapper));
    }
    public async Task<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken, IQueryable<T> query)
    {
        var dataServiceQuery = query as DataServiceQuery<T>;
        if (query == null)
            return query.ToArray();

        return await dataServiceQuery.ExecuteAsync();
    }

    public IEnumerable<T> Get<T>(Expression<Func<TEntity, T>> selectStatement,
        Expression<Func<TEntity, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<TEntity> orderByWrapper = null,
        IncludeWrapper<TEntity> includeWrapper = null)
    {
        //DataServiceCollection does not do mapping within the query, so the expression must be compiled and passed to the select
        var select = selectStatement.Compile();
        return new DataServiceCollection<TEntity>(CommonQuerySetup(filter,ref page,orderByWrapper,includeWrapper)).AsEnumerable().Select(select);
    }

    public Task<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken, Expression<Func<TEntity, T>> selectStatement, Expression<Func<TEntity, bool>> filter = null,
        Page page = default(Page),
        OrderByWrapper<TEntity> orderByWrapper = null,
        IncludeWrapper<TEntity> includeWrapper = null)
    {
        return Task.FromResult(Get(selectStatement,filter, page, orderByWrapper));
    }

    //NOTE: custom implementation of the find method may be created

    public TEntity GetById(object id)
    {
        var entityUri = new Uri( System.IO.Path.Combine(System.IO.Path.Combine(_context.BaseUri.AbsoluteUri, typeof(TEntity).Name)) + "(" + id.ToString() +")" );
        TEntity entity = null;
        if ( !_context.TryGetEntity(entityUri, out entity))
        {
            try
            {
                entity = _context.Execute<TEntity>(entityUri, "GET").FirstOrDefault();
            }
            catch (DataServiceClientException dscEx)
            {
                return default(TEntity);
            }
        }

        return entity;
    }

    public async Task<TEntity> GetByIdAsync(CancellationToken cancellationToken, object id)
    {
        var entityUri = new Uri(System.IO.Path.Combine(_context.BaseUri.AbsoluteUri, typeof(TEntity).Name) + "(" + id.ToString() + ")");
        TEntity entity = null;
        if (!_context.TryGetEntity(entityUri, out entity))
        {
            var entities = await _context.ExecuteAsync<TEntity>(entityUri, "GET");
            entity = entities.FirstOrDefault();
        }

        return entity;
    }

    public TEntity GetSingle(Expression<Func<TEntity, bool>> filter)
    {
        return (new DataServiceCollection<TEntity>(_set.Where(filter)).Single());
    }

    public Task<TEntity> GetSingleAsync(CancellationToken cancellationToken, Expression<Func<TEntity, bool>> filter)
    {
        return Task.FromResult(GetSingle(filter));
    }

    public TEntity Update(TEntity entity, bool saveImmediately = false)
    {
        var dataCollection = new DataServiceCollection<TEntity>(new TEntity[] { entity });
        if (saveImmediately)
        {
            _context.SaveChanges();
        }
        return entity;
    }

    public async Task<TEntity> UpdateAsync(CancellationToken cancellationToken, TEntity entity, bool saveImmediately = false)
    {

        //var setName = this._context.ResolveName(typeof(TEntity));

        //this._context.AttachTo(this._context.Entities.Where(x => x.Entity.GetType() == typeof(TEntity)).First().ETag, entity);
        this._context.UpdateObject(entity);

        if (saveImmediately)
        {
            await _context.SaveChangesAsync();
        }
        return entity;
    }

    public TEntity Update(object id, Action<TEntity> updateAction, bool saveImmediately = false)
    {
        var entity = this.GetById(id);
        if( entity != null )
        {
            entity = this.Update(entity, saveImmediately);
        }

        return entity;
    }

    public async Task<TEntity> UpdateAsync(CancellationToken cancellationToken, object id, Action<TEntity> updateAction, bool saveImmediately = false)
    {
        var entity = await this.GetByIdAsync(cancellationToken, id);
        updateAction(entity);
        if (entity != null)
        {
            entity = await this.UpdateAsync(cancellationToken, entity, saveImmediately);
        }

        return entity;
    }

    public TEntity Create(TEntity entity, bool saveImmediately = false)
    {
        var dataCollection = new DataServiceCollection<TEntity>(new TEntity[] { entity });
        if (saveImmediately)
        {
            _context.SaveChanges();
        }
        return entity;
    }

    public async Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancellationToken, bool saveImmediately = false)
    {
        var dataCollection = new DataServiceCollection<TEntity>(new TEntity[] { entity });
        if (saveImmediately)
        {
            await _context.SaveChangesAsync();
        }
        return entity;
    }

    public void Delete(TEntity entity, bool saveImmediately = false)
    {
        var dataCollection = new DataServiceCollection<TEntity>(new TEntity[] { entity });
        _context.DeleteObject(entity);
        if (saveImmediately)
        {
            _context.SaveChanges();
        }
    }

    public async Task DeleteAsync(CancellationToken cancellationToken, TEntity entity, bool saveImmediately = false)
    {
        var dataCollection = new DataServiceCollection<TEntity>(new TEntity[] { entity });
        _context.DeleteObject(entity);
        if (saveImmediately)
        {
            await _context.SaveChangesAsync();
        }
    }

    public void Delete(object id, bool saveImmediately = false)
    {
        var entity = this.GetById(id);
        this.Delete(entity, saveImmediately);
    }

    public async Task DeleteAsync(CancellationToken cancellationToken, object id, bool saveImmediately = false)
    {
        var entity = await this.GetByIdAsync(cancellationToken, id);
        await this.DeleteAsync(cancellationToken, entity, saveImmediately);
    }

    public void Dispose()
    {
        //empty, nothing to dispose
    }

    private void AddCachedQueryPropertyPlan()
    {
        //get property of type DataServiceQuery<TEntity>
        var property = _context.GetType().GetProperties().Where(x => x.PropertyType == typeof(DataServiceQuery<TEntity>)).FirstOrDefault();
        if (property == null)
        {

            _cachedPlans[typeof(TEntity)] = new Func<DataServiceContext, DataServiceQuery>(x => x.CreateQuery<TEntity>(typeof(TEntity).Name));
        }
        else
        {
            //create an expression to grab a reference to that property
            var arg = Expression.Parameter(_context.GetType(), "x");
            var body = Expression.PropertyOrField(arg, property.Name);
            var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(_context.GetType(), typeof(DataServiceQuery)),
                body, "PropertyExpression", new List<ParameterExpression> { arg });

            //compile that expression and add it to the static dictionary
            var compiledExpression = lambda.Compile();
            _cachedPlans[typeof(TEntity)] = new Func<DataServiceContext, DataServiceQuery>(x => (compiledExpression.DynamicInvoke(x) as DataServiceQuery));
        }
    }

    protected IQueryable<TEntity> CommonQuerySetup(Expression<Func<TEntity, bool>> filter, ref Page page, OrderByWrapper<TEntity> orderByWrapper, IncludeWrapper<TEntity> includeWrapper)
    {
        IQueryable<TEntity> queryable = this.Queryable;
        if (filter != null)
        {
            queryable = queryable.Where(filter);
        }
        if (orderByWrapper != null)
        {
            foreach (var orderby in orderByWrapper.OrderByClauses)
            {
                queryable = orderby(queryable);
            }
        }
        if (page != default(Page))
        {
            if (orderByWrapper == null)
            {
                throw new NotSupportedException("Paging not supported without an order by clause");
            }
            else
            {
                queryable = queryable.Skip((page.PageNumber - 1) * page.PageSize);
                queryable = queryable.Take(page.PageSize);
            }
        }
        return queryable;
    }

    public IEnumerable<TEntity> Local
    {
        get
        {
            return _context.Entities.Where(x => x.Entity.GetType() == typeof(TEntity)).Select(x => (TEntity)x.Entity);
        }
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    public Task SaveAsync(CancellationToken cancellationToken)
    {
        return _context.SaveChangesAsync();
    }
    public virtual void Undo<TEntityType>(TEntityType entity) where TEntityType : class
    {
        var ed = _context.GetEntityDescriptor(entity);
        if( ed != null && ed.State != EntityStates.Unchanged && ed.State != EntityStates.Detached)
        {
            _context.Detach(entity);
            if (ed.State != EntityStates.Added)
            {
                _context.Execute<TEntityType>(ed.SelfLink);
            }
        }

    }
    public virtual async Task UndoAsync<TEntityType>(CancellationToken cancellationToken, TEntityType entity) where TEntityType : class
    {
        var ed = _context.GetEntityDescriptor(entity);
        if (ed != null && ed.State != EntityStates.Unchanged && ed.State != EntityStates.Detached)
        {
            _context.Detach(entity);
            if (ed.State != EntityStates.Added)
            {
                await _context.ExecuteAsync<TEntityType>(ed.SelfLink);
            }
        }
    }

    public virtual void UndoAll()
    {
        foreach (var entity in _context.Entities)
        {
            this.Undo(entity.Entity);
        }
    }
    public virtual async Task UndoAllAsync(CancellationToken cancellationToken)
    {
        foreach (var entity in _context.Entities)
        {
            await this.UndoAsync(cancellationToken, entity.Entity);
        }
    }

#if DEBUG
    void _context_SendingRequest2(object sender, SendingRequest2EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine(e.RequestMessage.Url);
    }

    void _context_ReceivingResponse(object sender, ReceivingResponseEventArgs e)
    {
        //var stream = e.ResponseMessage.GetStream();
        //var reader = new System.IO.StreamReader(stream);
        //System.Diagnostics.Trace.WriteLine(reader.ReadToEnd());
    }
#endif

    public Task<IEnumerable<TEntity>> OperateAsync<T>(Func<IQueryable, IQueryable> func, CancellationToken cancellationToken)
    {
        var masterGames = func(_set)
                                .OfType<TEntity>();

        return GetAsync(cancellationToken, masterGames);
    }

    public IQueryable Get(Func<IQueryable, IQueryable> queryFunc)
    {
        return queryFunc.Invoke(this.Queryable);
    }

    public Task<IEnumerable<TEntity>> Get(Func<IQueryable, IQueryable> queryFunc, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    public Task<IQueryable<TEntity>> GetAsync(CancellationToken cancellationToken, Func<IQueryable, IQueryable> queryFunc)
    {
        throw new NotImplementedException();
    }

    public IQueryable<TDataObject> Get<TDataObject>(Func<IQueryable, IQueryable<TDataObject>> queryFunc)
    {
        throw new NotImplementedException();
    }

    public virtual void Load<TRelatedEntity>(TEntity entity, Expression<Func<TEntity, TRelatedEntity>> relation) where TRelatedEntity : class
    {
        string propertyPath = ESL.Core.Helpers.Utilities.ParsePropertySelector(relation);
        if( propertyPath == null )
            throw new ArgumentException("relation Expression does not reference a Property on type: " + typeof(TEntity).Name);

        _context.LoadProperty(entity, propertyPath);
    }
    public virtual void Load<TRelatedEntity>(TEntity entity, Expression<Func<TEntity, ICollection<TRelatedEntity>>> relation) where TRelatedEntity : class
    {
        string propertyPath = ESL.Core.Helpers.Utilities.ParsePropertySelector(relation);
        if (propertyPath == null)
            throw new ArgumentException("relation Expression does not reference a Property on type: " + typeof(TEntity).Name);

        _context.LoadProperty(entity, propertyPath);
    }
    public virtual async Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, TEntity entity, Expression<Func<TEntity, TRelatedEntity>> relation) where TRelatedEntity : class
    {
        string propertyPath = ESL.Core.Helpers.Utilities.ParsePropertySelector(relation);
        if (propertyPath == null)
            throw new ArgumentException("relation Expression does not reference a Property on type: " + typeof(TEntity).Name);

        var queryTask = Task.Factory.FromAsync(_context.BeginLoadProperty(entity, propertyPath, null, null),
                (loadPropertyAsyncResult) =>
                {
                    var results = _context.EndLoadProperty(loadPropertyAsyncResult);
                    return ;
                });

        await queryTask;
        return;
    }
    public virtual async Task LoadAsync<TRelatedEntity>(CancellationToken cancellationToken, TEntity entity, Expression<Func<TEntity, ICollection<TRelatedEntity>>> relation) where TRelatedEntity : class
    {
        string propertyPath = ESL.Core.Helpers.Utilities.ParsePropertySelector(relation);
        if (propertyPath == null)
            throw new ArgumentException("relation Expression does not reference a Property on type: " + typeof(TEntity).Name);

        var queryTask = Task.Factory.FromAsync(_context.BeginLoadProperty(entity, propertyPath, null, null),
                (loadPropertyAsyncResult) =>
                {
                    var results = _context.EndLoadProperty(loadPropertyAsyncResult);
                    return;
                });

        await queryTask;
        return;

    }
}

With both implementations defined, they can be easily swapped and injected at runtime.

Twitter Auto Publish Powered By : XYZScripts.com