Entity inheritance vs ObjectQuery vs QueryName

12 posts, 1 answers
  1. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 03 Apr 2012 Link to this post

    I have two entities, for instance entityA and entityB inherited from entityB. In ObjectContext there is
    ObjectSet<entityA> entityAs;
    But I need an ObjectResult<entityB>, and has created this (custom) property on ObjectContext:
    ObjectResult<entityB> entityBs
    { ... }
    So when I user entityBs as QueryName in RadEntityFrameWorkDataSource (in XAML), then nearly all works fine except AddNew, because the QueryName is used for getting ObjectResult<> property value of ObjectContext, and for entitySetName.
    Can I somehow enforce the real base entit class's set name for use as entitySetName, and (any) ObjectQuery as object query used to create QueryabelEntityCollection?

    It is not a solution, when I use for QueryName the base ObjectSet's name (and approperiate FilterDescription), because the AddNew produce a base class instance not a derived class's one. 

    (ok, I can create manually QueryabelEntityCollection<entityB>, but it is not real beuty solution :))
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 03 Apr 2012 Link to this post

    Hello,

    I am afraid that we will significantly mess up the public API of RadEntityFrameworkDataSource if we add yet another property besides the QueryName.

    Anyway, the control is simply a very thin wrapper over QueryableEntityCollectionView<T>, so my suggestion would be to use this collection if possible.

    Thank you for your understanding.

    Regards,
    Ross
    the Telerik team
    Sharpen your .NET Ninja skills! Attend Q1 webinar week and get a chance to win a license! Book your seat now >>
  3. UI for WPF is Visual Studio 2017 Ready
  4. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 05 Apr 2012 Link to this post

    Hi,
    I think does not need implement any plus property. In RadEntityFrameworkDataSource's TryRefreshView method can get all need info, like this (entityType is get in TryRefreshView):
    Type typeParent = entityType;             
    while (typeParent != null || typeParent.BaseType != typeof(EntityObject))
    typeParent = typeParent.BaseType;             
    if (typeParent == null)
    //exception, targettype is not EntityObject             
    Type osetType = typeof(ObjectQuery<>).MakeGenericType(new Type[] {typeParent});
    var esv = from PropertyInfo pi in objectQuery.GetProperties()
              where pi.PropertyType.IsSubclassOf(osetType)
            select pi;
    ObjectQuery oquery = esv.First().GetValue(ObjectContext , nullas ObjectQuery;
    System.Data.Metadata.Edm.EntitySet es = oquery.GetType().GetProperty("EntitySet").GetValue(oquery, nullas System.Data.Metadata.Edm.EntitySet;
    string realqueryname = es.Name;

    In realqueryname we can found the queryname of top parent class's query name, so can use it in QueryableCollection view instance creation.
    And when the ObjectQueryProxy's Add method will be called, then the right entitySetName will be used.

    And of course the code like above can be place into an if (entityType.BaseType != typeof(EntityObject))
     blokk, and all the usage of RadEntityFrameworkDataSource worked before will be work after same way.
    K2
  5. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 05 Apr 2012 Link to this post

    Hello,

    I will consider your suggestion and will see whether it can be implemented without any breaking changes. For this purpose, can you please send me a small dummy project with your exact architecture so I can test against. You can use Northwind or AdventureWorks for the sake of demonstration.

    Thank in advance.

    Kind regards,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  6. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 05 Apr 2012 Link to this post

    I've create a short application.
    You can download from https://skydrive.live.com/redir.aspx?cid=b1465de519409505&resid=B1465DE519409505!139&parid=B1465DE519409505!137

    remark 1.: the determination of real entitysetname is in refdEmployees_Loaded  in MainWindow.xaml.cs, and replace the View property, and the binding to gridview and dataform created after it
    remark 2: in DataForm Escape the newly created entity will not remove from datasoure (not in this app nor my business app)
    remark 3: if I use auto generated fields, then the Cancel will throw some exception (MoveLast pehaps)
    remark 4: I think You have to modify connection string in app.config (SQL Server name)
    remark 5: the newly created object is a copy of another one, I do'nt know while, in my business application the new object is empty (always).

    K2
  7. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 05 Apr 2012 Link to this post

    I'v created a new sample, this is include a hacked RadEntiityFrameworkDataSource, you can see: https://skydrive.live.com/?cid=b1465de519409505&id=B1465DE519409505%21140
    It works - just some problem in Cancel, I do'nt while try set the properties of dropping object (and it is a non nullable string property).

    K2
  8. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 06 Apr 2012 Link to this post

    Hi,

    I will debug the code that you have sent me and see exactly what it does.

    Meanwhile, I have a question for you. I've seen that you are acquainted with the source code, so suppose that I take this piece of code from the TryRefreshView method:

    // ObjectQuery<Customer>
    var propertyInfo = this.ObjectContext
        .GetType()
        .GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .FirstOrDefault(pi => pi.Name == this.QueryName && IsValidObjectQueryProperty(pi));
     
    if (propertyInfo != null)
    {
        // Customer
        var entityType = propertyInfo.PropertyType.GetGenericArguments().First();
     
        var objectQuery = propertyInfo.GetValue(this.ObjectContext, null);
     
        // QueryableEntityCollectionView<Customer>
        var genericQCVType = typeof(QueryableEntityCollectionView<>).MakeGenericType(new Type[] { entityType });
     
        // new QueryableEntityCollectionView<Customer>(objectQuery, this.QueryName, this.RelatedObjects)
        this.View =
            (QueryableCollectionView)Activator.CreateInstance(genericQCVType
            , objectQuery
            , this.QueryName
            , this.RelatedObjects);
     
        return true;
    }

    and I extract this to a new protected virtual method called CreateView or something like this, will you be able to derive from RadEntityFrameworkDataSource and override this method. In this way you will have the full and total freedom to create the view in any custom way that you want. And of course do the error handling appropriately. 

    In this way I will keep the control just as it is (i.e. no possible breaking changes for anyone else) and give you the ability ("with great power comes great responsibility") to "hack" the creating of the view.

    What do you think? Will this be a viable solution for you case?
    Regards,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  9. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 06 Apr 2012 Link to this post

    Yes, the new protected virtual CreateView method would be a best! :)
    While? Because I can derive not just RadEntityFrameworkDataSource and override CreateView, but it is possible to create QueryabelEntityCollectionView<T> derived view if needed.
    So, your solution will be more general then my base problem reuires.

    What do you think, when will be reachable the build including this modification?
    K2
  10. Answer
    Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 06 Apr 2012 Link to this post

    Hi,

    I have prepared an unofficial build with this change on my computer and updated the sample project that you sent me. Please give it a try and tell me whether it matches your requirements. I have derived from RadEntityFrameworkDataSource and called the derived class MyREFDS. Currently, I have copied the base implementation in the overloaded method which you can start tweaking around to see whether everything will be fine.

    Here is how I changed the code:

    private bool TryRefreshView()
    {
        if (this.ObjectContext == null || string.IsNullOrEmpty(this.QueryName))
        {
            return false;
        }
     
        if (RadControl.IsInDesignMode)
        {
            this.View = new QueryableCollectionView(Enumerable.Empty<object>());
            return true;
        }
     
        var result = this.CreateView();
     
        if (result != null)
        {
            this.View = result;
            return true;
        }
     
        return false;
    }
     
    /// <summary>
    /// Creates the view.
    /// </summary>
    /// <returns>The view.</returns>
    protected virtual QueryableCollectionView CreateView()
    {
        // ObjectQuery<Customer>
        var propertyInfo = this.ObjectContext
            .GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .FirstOrDefault(pi => pi.Name == this.QueryName && IsValidObjectQueryProperty(pi));
     
        if (propertyInfo != null)
        {
            // Customer
            var entityType = propertyInfo.PropertyType.GetGenericArguments().First();
     
            var objectQuery = propertyInfo.GetValue(this.ObjectContext, null);
     
            // QueryableEntityCollectionView<Customer>
            var genericQCVType = typeof(QueryableEntityCollectionView<>).MakeGenericType(new Type[] { entityType });
     
            // new QueryableEntityCollectionView<Customer>(objectQuery, this.QueryName, this.RelatedObjects)
            var result =
                (QueryableCollectionView)Activator.CreateInstance(genericQCVType
                , objectQuery
                , this.QueryName
                , this.RelatedObjects);
     
            return result;
        }
     
        throw new ArgumentException(string.Format("Cannot find a valid ObjectQuery named {0} on the ObjectContext.", this.QueryName));
    }
     
    /// <summary>
    /// Determines whether the property info is valid ObjectQuery.
    /// </summary>
    /// <param name="propertyInfo">The property info.</param>
    /// <returns>
    ///     <c>true</c> if the property info is valid ObjectQuery; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsValidObjectQueryProperty(PropertyInfo propertyInfo)
    {
        return propertyInfo.PropertyType.IsGenericType
            && typeof(ObjectQuery).IsAssignableFrom(propertyInfo.PropertyType)
            && propertyInfo.PropertyType.GetGenericArguments().Count() == 1
            && typeof(INotifyPropertyChanged).IsAssignableFrom(propertyInfo.PropertyType.GetGenericArguments().First());
    }

    And the overriden method that you can now do anything with:

    public class MyREFDS : RadEntityFrameworkDataSource
    {
        protected override Telerik.Windows.Data.QueryableCollectionView CreateView()
        {
            // ObjectQuery<Customer>
            var propertyInfo = this.ObjectContext
                .GetType()
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .FirstOrDefault(pi => pi.Name == this.QueryName && IsValidObjectQueryProperty(pi));
     
            if (propertyInfo != null)
            {
                // Customer
                var entityType = propertyInfo.PropertyType.GetGenericArguments().First();
     
                var objectQuery = propertyInfo.GetValue(this.ObjectContext, null);
     
                // QueryableEntityCollectionView<Customer>
                var genericQCVType = typeof(QueryableEntityCollectionView<>).MakeGenericType(new Type[] { entityType });
     
                // new QueryableEntityCollectionView<Customer>(objectQuery, this.QueryName, this.RelatedObjects)
                var view =
                    (QueryableCollectionView)Activator.CreateInstance(genericQCVType
                    , objectQuery
                    , this.QueryName
                    , this.RelatedObjects);
     
                return view;
            }
     
            return null;
        }
    }

     If it works fine for you, I will check in source control and hopefully on Monday you will get it "officially" with our next "Latest Internal Build". Please, test all corner cases that you might think of.

    I am looking forward to hearing from you.

    All the best,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  11. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 06 Apr 2012 Link to this post

    OK, I have tried it, and think it more then good enough!
    Here it is my overridden CreateView method:
     protected override Telerik.Windows.Data.QueryableCollectionView CreateView()
    {
    // ObjectQuery<Customer>
    var propertyInfo = this.ObjectContext
    .GetType()
    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
    .FirstOrDefault(pi => pi.Name == this.QueryName && IsValidObjectQueryProperty(pi));

    if (propertyInfo != null)
    {
    // Customer
    var entityType = propertyInfo.PropertyType.GetGenericArguments().First();

    var objectQuery = propertyInfo.GetValue(this.ObjectContext, null);

    // QueryableEntityCollectionView<Customer>
    var genericQCVType = typeof(QueryableEntityCollectionView<>).MakeGenericType(new Type[] { entityType });

                    string _queryName = this.QueryName;
                    //perhaps less is enough then EntityObject, I do'nt know
                    if (entityType.BaseType != typeof(EntityObject))
                    {
                        Type typeParent = entityType;
                        while (typeParent != null && typeParent.BaseType != typeof(EntityObject))
                            typeParent = typeParent.BaseType;
                        if (typeParent == null)
                            throw new Exception("ObjectQuery's generic argument is not EntityObject");
                        Type osetType = typeof(ObjectQuery<>).MakeGenericType(new Type[] { typeParent });
                        var esv = from PropertyInfo pi in ObjectContext.GetType().GetProperties()
                                  where pi.PropertyType.IsSubclassOf(osetType)
                                  select pi;
                        ObjectQuery oquery = esv.First().GetValue(ObjectContext, nullas ObjectQuery;
                        EntitySet es = oquery.GetType().GetProperty("EntitySet").GetValue(oquery, nullas EntitySet;
                        _queryName = es.Name;
                    }
                    // new QueryableEntityCollectionView<Customer>(objectQuery, this.QueryName, this.RelatedObjects)
    var view =
    (QueryableCollectionView)Activator.CreateInstance(genericQCVType
    , objectQuery
                        , _queryName
    this.RelatedObjects);

    return view;
    }

    return null;
    }

    K2
  12. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 06 Apr 2012 Link to this post

    Hello,

    I have checked-in the source code and it will be available with our next Latest Internal Build.

    I hope this will help you move forward with your project.

    Regards,
    Ross
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  13. Kovács
    Kovács avatar
    7 posts
    Member since:
    Oct 2008

    Posted 06 Apr 2012 Link to this post

    Thanks!

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