Fetch strategy not working for Generated Data Access Repositories for WebAPI

7 posts, 0 answers
  1. pomi
    pomi avatar
    5 posts
    Member since:
    Apr 2008

    Posted 09 Jun 2014 Link to this post

    Hello

    I am having problem with Include() and FetchStrategies.

    What did i do :

    -  Following database first approach, Created a Domain Model using Telerik Data Access. This resulted in 'rlinq' file.
    -  Since I was going to implement repository pattern and the quickest way I found with Telerik Data Access was that, I right click the rlinq file, and select "Create Telerik Data Access Service...'
    -  

    Now I have e.g. an autogenerated entity by the rlinq file

    01.public partial class Approval
    02.{
    03.    private long _approvalId;
    04.    public virtual long ApprovalId
    05.    {
    06.        get
    07.        {
    08.            return this._approvalId;
    09.        }
    10.        set
    11.        {
    12.            this._approvalId = value;
    13.        }
    14.    }
    15.     
    16.    private long? _clientId;
    17.    public virtual long? ClientId
    18.    {
    19.        get
    20.        {
    21.            return this._clientId;
    22.        }
    23.        set
    24.        {
    25.            this._clientId = value;
    26.        }
    27.    }
    28.     
    29.    private string _subscriptionId;
    30.    public virtual string SubscriptionId
    31.    {
    32.        get
    33.        {
    34.            return this._subscriptionId;
    35.        }
    36.        set
    37.        {
    38.            this._subscriptionId = value;
    39.        }
    40.    }
    41. 
    42.    private Company _company;
    43.    public virtual Company Company
    44.    {
    45.        get
    46.        {
    47.            return this._company;
    48.        }
    49.        set
    50.        {
    51.            this._company = value;
    52.        }
    53.    }
    54. 
    55.    private IList<Calling> _callings = new List<Calling>();
    56.    public virtual IList<Calling> Callings
    57.    {
    58.        get
    59.        {
    60.            return this._callings;
    61.        }
    62.    }

    I the base repository interface (note this is also auto generated)

    1.public interface IOpenAccessBaseRepository<TEntity, TContext> where TContext : OpenAccessContext, new()
    2.{
    3.    IQueryable<TEntity> GetAll();
    4.    TEntity GetBy(Expression<Func<TEntity, bool>> filter);
    5.    TEntity AddNew(TEntity entity);
    6.    TEntity Update(TEntity entity);
    7.    void Delete(TEntity entity);
    8.}

    Concrete implementation of above base repository, all other repositories are derived from this base repository (from the base repository, i am pasting implementation of only two GET functions for brevity)

    01.    public abstract partial class OpenAccessBaseRepository<TEntity, TContext> : IOpenAccessBaseRepository<TEntity, TContext> where TContext : OpenAccessContext, new()
    02.    {
    03.        protected TContext dataContext = new TContext();
    04.        protected FetchStrategy fetchStrategy = new FetchStrategy();
    05. 
    06.        public IQueryable<TEntity> GetAll()
    07.        {
    08.            List<TEntity> allEntities = dataContext.GetAll<TEntity>().ToList();
    09. 
    10.            List<TEntity> detachedEntities = dataContext.CreateDetachedCopy<List<TEntity>>(allEntities, fetchStrategy);
    11. 
    12.            return detachedEntities.AsQueryable();
    13.        }
    14. 
    15.        public TEntity GetBy(Expression<Func<TEntity, bool>> filter)
    16.        {
    17.            if (filter == null)
    18.                throw new ArgumentNullException("filter");
    19. 
    20.            TEntity entity = dataContext.GetAll<TEntity>().SingleOrDefault(filter);
    21.            if (entity == null)
    22.                return default(TEntity);
    23. 
    24.            TEntity detachedEntity = dataContext.CreateDetachedCopy(entity, fetchStrategy);
    25. 
    26.            return detachedEntity;
    27.        }
    28.}

    A Derived Repository from the above Base Repository
    01.public partial class ApprovalRepository : OpenAccessBaseRepository<Approval, VizAppModel>
    02.{
    03.     public new IQueryable<Approval> GetAll()
    04.    {
    05.        this.fetchStrategy.LoadWith<Approval>(a => a.Company);
    06.        List<Approval> allEntities = dataContext.GetAll<Approval>().ToList();
    07. 
    08.        List<Approval> detachedEntities = dataContext.CreateDetachedCopy<List<Approval>>(allEntities, fetchStrategy);
    09. 
    10.        return detachedEntities.AsQueryable();
    11.    }
    12. 
    13.}

    PROBLEM NOTE : the above repository is never actually used, this new GetAll() function is never called, instead GetAll() function from the base repository is called by the program. I am using a Service Layer, this is simple class calling from repositories as follows:

    The Service Classes derive from an Interface as follows:
    01.public interface IVizAppService<TEntity, TContext> where TContext : OpenAccessContext, new ()
    02.{
    03.    IQueryable<TEntity> GetAll(string sid);
    04.    TEntity GetById(object id, string sid);
    05.    TEntity AddNew(TEntity entity, string sid);
    06.    TEntity Update(TEntity entity);
    07.    void Delete(TEntity entity);
    08.    void HardDelete(TEntity entity);
    09.}

    Concrete implementation of this IVizAppService .... the methods in this class are called by controllers directly
    01.public class ApprovalService : IVizAppService<Approval, VizAppModel>
    02.{
    03.    protected IOpenAccessBaseRepository<Approval, VizAppModel> service;
    04. 
    05.    public ApprovalService()
    06.    {
    07.        this.service = new ApprovalRepository();
    08.    }
    09.    public ApprovalService(IOpenAccessBaseRepository<Approval, VizAppModel> service)
    10.    {
    11.        this.service = service;
    12.    }
    13. 
    14.     
    15.    public IQueryable<Approval> GetAll(string sid)
    16.    {
    17.        return service.GetAll()
    18.            .Where(i => i.SubscriptionId == sid && i.IsActive == true);
    19.    }
    20. 
    21.    public Approval GetById(object id, string sid)
    22.    {
    23.        if (id == null)
    24.        {
    25.            throw new ArgumentNullException("id");
    26.        }
    27.        return service.GetAll().Include(i => i.Company)
    28.             .SingleOrDefault(i => i.ApprovalId == (long)id);
    29.    }


    PROBLEM THAT I AM FACING AND CANNOT SOLVE = FETCHSTRATEGY OR 'INCLUDE' NOT WORKING
     
    my GetAll methods in above service class, despite using 'Include' still cant get related entity 'Company' in the calls. I have tried a lot using fetch strategy but I know there is something wrong with the architecture or i just cant get my head around it. Most of the code is boilercode which is added automatically.

    PLEASE HELP ..... If i could drop the service layer and use Enity Repository directly, i would gladly do so, but since i cant get 'repository class' to reference in my controllers, i used this above service layer.

    Thanks for your help

    Ali










  2. Boris Georgiev
    Admin
    Boris Georgiev avatar
    190 posts

    Posted 12 Jun 2014 Link to this post

    Hi Ali,

    There is no need to override GetAll() method to introduce a FetchStrategy to the endpoint. To implement and use a FetchStrategy for a controller you should write the default constructor for the Repository in a partial class and define the FetchStrategy declared in the base class OpenAccessBaseRepository.

    In your case it should looks like:
    1.public partial class ApprovalRepository
    2.{
    3.    public ApprovalRepository()
    4.    {
    5.        this.fetchStrategy.LoadWith<Approval>(a => a.Company);
    6.    }
    7.}

    Thus the GetAll() method from the OpenAccessBaseRepository class will use the defined FetchStrategy from the inherited class - ApprovalRepository.

    I hope that helps.

    Regards,
    Boris Georgiev
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
  3. DevCraft banner
  4. pomi
    pomi avatar
    5 posts
    Member since:
    Apr 2008

    Posted 13 Jun 2014 Link to this post

    Hello Boris

    Thanks for your reply, Yes! this does solve the problem, now i am able to use the fetchStrategy. 

    Many Thanks

    Regards,
    Ali
    Webify Asia
  5. pomi
    pomi avatar
    5 posts
    Member since:
    Apr 2008

    Posted 09 Jul 2014 Link to this post

    Hello Boris

    As usual with Telerik, the reply you posted helped very well with our solution, however I stumble upon yet another question that i could not seems to find in the documents.

    Currently, applying the above solution, we ALWAYS get the navigation entity with the parent entity. e.g. having fetchStrategy defined in Repository constructor, we ALWAYS get 'Company' when we query 'Approval'.  This doesnt really give us any control on when we want to include the navigation entities (company) and when not. 

    Having used 'Include()' method, we had this control. Can you please advise how can we EXCLUDE the navigation entity (company) if we want to query for ONLY 'Approval' entity?

    I hope I am able to convey my point to you! Telerik support had been great and still rocks, Thanks in advance
  6. Ralph Waldenmaier
    Admin
    Ralph Waldenmaier avatar
    202 posts

    Posted 11 Jul 2014 Link to this post

    Hi pomi,
    Yes you are right. With the previous approach you are always getting it.
    You can try to do the following.
    public Approval GetById(object id, string sid)
    {
        if (id == null)
        {
            throw new ArgumentNullException("id");
        }
        var oldFetchStrategy = service.FetchStrategy;
        Approval result = null;
        try
        {
            // define a fetch strategy for the current call only
            var newFetchStrategy = new Telerik.OpenAccess.FetchOptimization.FetchStrategy();
            newFetchStrategy.LoadWith<Approval>(a => a.Company);
            service.FetchStrategy = newFetchStrategy;
             
            result = service.GetAll().SingleOrDefault(i => i.ApprovalId == (long)id);  
         }
         finally
         {
            service.FetchStrategy = oldFetchStrategy;
         }
          
         return result;
    }
    Here I assume that you don't have modified the FetchPlan in the ctor of the ApprovalRepository class.
    Instead here I am changing the default fetch plan for the current operation on the particular context. After the query is executed, the "old" FetchPlan is applied to the context again. A query that is fired after this will then no longer include the related Company object.

    Hope this helps in your environment.
    Do come back in case you need further assistance.

    Regards,
    Ralph Waldenmaier
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
  7. pomi
    pomi avatar
    5 posts
    Member since:
    Apr 2008

    Posted 11 Jul 2014 in reply to Ralph Waldenmaier Link to this post

    Hello Ralph

    Thanks for your help, Yes, this seems to be a good workout in our environment, however, we have some problem. 

    We are not able to access 'fetchStrategy' defined in 'OpenAccessBaseRepository' (code in my original question).

    We are able to access 'fetchStrategy' in the 'ApprovalRepository' or 'CompanyRepository' which is derived from 'OpenAccessBaseRepository'. 

    Even though in 'ApprovalService' constructor we declare 'this.service = new ApprovalRepository()' but still there is no 'service.fetchStrategy' !!!

    I know there is something i am missing over here but I am just getting confused as to how to wrap my head around it.

    Thanks for your continuous support, please can you give some insight!

    Regards

  8. Ralph Waldenmaier
    Admin
    Ralph Waldenmaier avatar
    202 posts

    Posted 15 Jul 2014 Link to this post

    Hi pomi,
    You can access the FetchStragegy by creating a new partial class for the generated OpenAccessBaseRepository and expose the FetchStrategy via a property.
    See the following example:
    public abstract partial class OpenAccessBaseRepository<TEntity, TContext> : IOpenAccessBaseRepository<TEntity, TContext>
            where TContext : OpenAccessContext, new()
        {
            public FetchStrategy FetchStrategy { get { return this.fetchStrategy; } set { this.fetchStrategy = value; } }
        }

    Hope this helps.


    Regards,
    Ralph Waldenmaier
    Telerik
     
    OpenAccess ORM is now Telerik Data Access. For more information on the new names, please, check out the Telerik Product Map.
     
Back to Top
DevCraft banner