EntitySetName with inheritance

14 posts, 0 answers
  1. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 23 Sep 2013 Link to this post

    Hello,

    We work on a project with EF5 (DbContext) and MVVM WPF Light Toolkit.
    So, to load data on the RadGridView, we try to use QueryableEntityCollectionView, instead of RadEntityFrameworkDataSource, it seems to be the good approach ?

    But when we want to include graph object when we load entities (with relatedObjectsToInclude in the ctor) when have a problem with the inheritance of the model because we have 2 levels. The message is : 

    An include path specified is not valid. The EntityType 'OPY.Entities.Sinistre' does not declare a navigation property with the name 'Agent'.

    The model :

    [Sinistre]
       |
    [SinistrePS]
    [----------]      
    [ AgentId  ] ---------- [Agent]
       |
    [Others class]

    The code :

    this.SinistreDataView = new QueryableEntityCollectionView<SinistrePS>(
      ((IObjectContextAdapter)_bdd).ObjectContext, "Sinistres", new List<string>() { "Agent" });

    As you can see, we want to load "SinitrePS" with all derived but we are forced to use "Sinistres" (top level) as entitySetName because the SinistrePS class don't have it own entityset on the model. 

    How can we do ?
    Thx

  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 23 Sep 2013 Link to this post

    Hello,

    I am afraid that with the QECV you cannot load more than the first level of related entities, i.e. the Include attribute is not drill-down.

    You can bind RadGridView directly to ObjectContext.Sinistres without using a QECV at all. Since it is an IQueryable, your queries will still be executed on the server.

    Regards,
    Rossen Hristov
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  3. UI for WPF is Visual Studio 2017 Ready
  4. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 23 Sep 2013 Link to this post

    thank you for the quick response

    In the application, we have a pager binded on the grid and the user can filter the grid on "Reference" property with a textbox.
    Here the code : 

    this.SinistreDataView.FilterDescriptors.Add(
         new FilterDescriptor("Reference", FilterOperator.Contains, textboxValue));

    How can we do this without QECV ?
  5. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 23 Sep 2013 Link to this post

    Hello,

    I am afraid that you can not. 

    By the way, can you show me how do you include those related Agents when not using any Telerik controls at all, i.e. when you work directly with the ObjectContext.

    We don't do anything special -- we simply append an Include clause based on the string that you provide in the ctor. Something like this:

    var result = this.ObjectContext.Sinistres.Include(s => s.Agent);

    That is what our code does when it sees your input string in the constructor. Unfortunately, you cannot do this on several levels since there is no way to express this desire.

    Regards,
    Rossen Hristov
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  6. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 23 Sep 2013 Link to this post

    You can do it like that

    My Context : 

    public partial class BddOpyContext : DbContext
    {
       public DbSet<Sinistre> Sinistres { get; set; } // First level
       public DbSet<SinistrePS> SinistrePSs { get; set; } // Second level
    }

    The code
    BddOpyContext _bdd
    retval = _bdd.SinistrePSs
            .Include("Agent")
            .OrderByDescending(at => at.Reference);

    Indeed we use DBContext and not ObjectContext ... and i don't know how to create the query to use ObjectContext ... (it's the realy problem)

    You could perhaps build the view from a DbContext ?
    The code would be :

    theDbContext.Set<T>().Include("includes")

    What do you think?
  7. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 23 Sep 2013 Link to this post

    Hello,

    I am somewhat confused.

    Well, the query that you just wrote would mean that you should pass "SinistrePSs" as the entity set name in the QECV constructor. Then we will append the Include clause on SinistrePS.

    This:

    retval = _bdd.SinistrePSs
            .Include("Agent");

    translates to

    this.SinistreDataView = new QueryableEntityCollectionView<SinistrePS>(
      ((IObjectContextAdapter)_bdd).ObjectContext, "SinistresPSs"new List<string>() {"Agent" });

    We take the string in yellow and look for such a query property on the ObjectContext with reflection. Then we append different clauses such as Where, OrderBy, Include, Skip and Take to the query property that we found.

    You are correctly passing in the ObjectContext. Even if there was a ctor accepting a DbContext, we do exactly the same -- obtain the ObjectContext by using the IObjectContextAdapter adapter.

    Regards,
    Rossen Hristov
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  8. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 23 Sep 2013 Link to this post

    Well, in fact you can't do this 

    As i said previousely  : we are forced to use "Sinistres" (top level) as entitySetName because the SinistrePS class don't have it own entityset on the model.

    In EF5, you don't have an EntitySetName for derived entity so if I use your code I have this message :

    Unable to resolve "SinistrePSs" in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly. Close simple identifier, row 1, column 1.

    You can see the model properties of table SinistrePS in the attached file
  9. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 23 Sep 2013 Link to this post

    You can pehpas use DBExtensions to include from IQueryable when we use QECV with DbContext ?
    http://msdn.microsoft.com/en-us/library/gg671236(v=vs.103).aspx

    This will perhaps less work than adapt the code completely for DbContext ?

    (just an idea, never used it)

  10. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 23 Sep 2013 Link to this post

    Hello,

    Can you send me a very little dummy sample project with you exact setup. I want to try several hacks to see whether something can be done about this scenario.

    The problem with Microsoft is that they started releasing EF out-of-band (independently from the .NET Framework). Each version they introduce breaking changes (like in the latest EF 6 RC alpha). You can imagine what this means for us as a component vendor.

    Once upon a time there was only ObjectContext and EF was part of the .NET Framework. We were able to build and test our component against something which is part of the standard .NET framework and does not change overnight. Suddenly, NuGet came along and MS started releasing all kinds of stuff like EF and WCF Data Service out-of-band from the .NET Framework. Now it is very hard for us to target all of their versions. Some customers use ObjectContext, other with EF 5 and higher use DbContext and the mess is complete. Furthermore, we have to build our assemblies against one of their versions -- which should we choose? It is really complicated.

    That is why when DbContext came about, we introduced this little hack for .NET Framework 4.5 only. We added a new property to RadEntityFrameworkDataSource called DbContext for the .NET 45 build only. If the developer supplies a DbContext, we simply obtain its ObjectContext by doing the same trick as you -- calling IObjectContextAdapater and then all the code remains the same because it is working with the legacy ObjectContext instance. 

    Here is the method of QECV that takes care of the Include clauses:

    private static ObjectQuery<T> AppendRelatedObjects(ObjectQuery<T> original, IEnumerable<string> relatedObjectsToInclude)
    {
        var result = original;
        foreach (var relatedObject in relatedObjectsToInclude)
        {
            result = result.Include(relatedObject);
        }
        return result;
    }

    The ObjectQuery<T> parameter that you see is taken by calling:

    objectContext.CreateQuery<T>(entitySetName);

    which in your case is objectContext.CreateQuery<T>("Sinistres");

    If I make this method AppendRelatedObject protected virtual, do you think you can override it in such a manner as to include the correct related objects?

    For the time being we do not plan on officially supporting DbContext in all its glory, since Entity Framework is no longer part of the .NET Framework and we can't really target a platform which is delivered through NuGet and has breaking changes in half of its nightly builds (read: this is a nightmare for a control vendor like us which has to be build and test everything half an year before an official release, which was the case with each new version of the NET Framework).

    By the way, isn't there any way to tell EF to skip lazy loading for certain properties, i.e. to tell EF directly to load the related Agents of each SinistrePS? In other way -- solve this problem at the EF level instead of trying to append an Include through our class, which is obviously not suited for the new DbContext paradigm. I think that if your collection property is not virtual if doing code-first approach, it will not be lazy loaded since EF will not be able to override it in a derived proxy and inject its lazy-loading logic. That is just an idea.

    Regards,

    Rossen Hristov
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  11. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 23 Sep 2013 Link to this post

    I understand your problems with the entiy framework version and I suspected it ...
    It seems to be nightmares

    For now, I remove the Include on the view and the grid do the work with the lazyloading, but for X lines, we have X call to the database : so bad perf !

    I will try your idea to override the method and I will create for you a small sample of my context
    And if all of this doesn't work, I'll have to develop my own DataView  :(

    we keep in touch
    thank you for your help
  12. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 25 Sep 2013 Link to this post

    Hello Rossen,

    I upload a zip file witch contains a small sample project with the bdd script
    here : http://speedy.sh/UpYMV/PocEfDataSource.zip (clik on the file name on the top of the page)

    I work with sql server 10.50, telerik 2013Q1

    I will try to override the datasource soon (we have your sources) and your help would be greatly appreciated 

    Thanks
  13. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 25 Sep 2013 Link to this post

    Hello,

    I have another idea. We already have a general purpose "data view". 

    We have a class called Telerik.Windows.Data.QueryableCollectionView. It accepts IEnumerable in its constructor, but if it is an IQueryable it will directly do queries over it. So you can try something like this:

    var query = this.dbContext.SinistrePSs.Include(s => s.Agents);
    var qcv = new QueryableCollectionView(query);

    Then bind both RadGridView and RadDataPager to this qcv instance. The pager will append Skip and Take's to your query, while the grid will append Where and OrderBy's when the user filters and sorts. 

    In other words, the grid and the pager will communicate with this QCV and it will translate their orders to LINQ queries on your original query which is this.dbContext.SinistrePSs.Include(s => s.Agents)

    It is like doing this: this.radGridView.ItemsSource = this.dbContext.SinistrePSs.Include(s => s.Agents);

    but since you need a pager, you place this QCV in the middle so you can bind both the grid and the pager to it.

    Alternatively, you can do the following:

    this.radGridView.ItemsSource = this.dbContext.SinistrePSs.Include(s => s.Agents);
    this.radDataPager.Source = this.radGridView.Items;

    And the pager will page the grid which will reach your DbContext eventually.

    Can you please try these two approaches and let me know?

    Regards,
    Rossen Hristov
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WPF.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  14. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 25 Sep 2013 Link to this post

    Sounds good, I will try
  15. JMazeran
    JMazeran avatar
    12 posts
    Member since:
    May 2012

    Posted 26 Sep 2013 Link to this post

    Hi,

    It seems to work fine :) here my code :

    var query = _bdd.SinistrePSs.Include("Agent");
    this.SinistreDataView = new QueryableCollectionView(query);
    this.SinistreDataView.SortDescriptors.Add(new SortDescriptor() { Member = "Reference", SortDirection = ListSortDirection.Descending });

    <tlk:RadGridView AutoGenerateColumns="False" GroupRenderMode="Flat"
       ItemsSource="{Binding SinistreDataView}" IsSynchronizedWithCurrentItem="False"
       SelectedItem="{Binding SinistreCourant}"
       IsReadOnly="True" ValidatesOnDataErrors="None" >

    <tlk:RadDataPager Grid.Row="1" PageSize="20"
       Source="{Binding SinistreDataView}"                      
       DisplayMode="FirstLastPreviousNextNumeric, Text"
       IsTotalItemCountFixed="True"  />

    I don't understand one thing : on my small poc : the load query is launch 2 times : 
    - after instanciate of QCV
    - when the grid is show
    But in my reality application, just one 1 time after instanciate ... Fortunately that is not the opposite !!

    So work done ! Thanks a lot Rossen
    Do you have seen my other post here : http://www.telerik.com/community/forums/wpf/autocompletebox/autocomplete-and-database-query.aspx ?

    Bye
Back to Top
UI for WPF is Visual Studio 2017 Ready