none
Repositories using IObjectSet<T> without deferredloading, how to include / loadoptions ?

    Question

  • I´m playing around with the POCO support.
    I want my repositories to take a dependency for the context via an interaface IMyContext.

    IMyContext:
    IObjectSet<Foo> Foos.
    IObjectSet<Bar> Bars.

    FooRepository(IMyContext context).

    That interface exposes my sets through IObjectSet<T>.
    In my repository I want to be albe to control my loading options, without deferredloading and proxies. Buy since I´m using IObjectSet I can´t use Include.

    Foo GetFoo(int Id)
    {
    return this.context.Foos.Include("Bars").SingeOrDefault(x => x.Id == id);
    }

    Any hints or tips how to conrol my loading options in a repository without using deferredloading and proxies ?

    I want to try to not use proxies due to later serialization. Maybe proxies and all is worth it and a I have to map to another or the same object to get rid of the proxies.
    AutoMapper Proxy (back to)-> Domain maybe something ?
    Maybe repositories just knowing IObjectSet<T> ain' t a good idea.

    Thanks!

    Sunday, July 19, 2009 8:07 PM

Answers

  • Hello,

    There a few things I know you can do. The solution I like the most is to define an Include extension method on IQueryable<T>:

        public static class ObjectQueryExtensions

        {

            public static IQueryable<T> Include<T>(this IQueryable<T> source, string path)

            {

                var objectQuery = source as ObjectQuery<T>;

                if (objectQuery != null)

                {

                    return objectQuery.Include(path);

                }

                return source;

            }

        }


    Notice that this extension method is defined on IQueryable<T>, and not on IObjectSet<T>, however, you can use it on any IObjectSet<T>, since IObjectSet<T> derives from IQueryable<T>. The reason I defined it on IQueryable<T> is that it is more useful there, since ObjectQuery<T>, which does not implement the full IObjectSet<T> interface, also has the capability to do eager loading. Also, the truth is that as soon as you apply any query operation to an ObjectSet<T>, you get an ObjectQuery instance, and you are no longer talking to an ObjectSet.

    Notice that this extension method will only have an effect when applied to an actual Entity Framework query object (either ObjectQuery<T> or ObjectSet<T>). The assumption is that you have control of the test data sets that you use in your unit tests and that you can always make sure that when you are running in-memory queries, your test-doubles will provide the whole graph (the semantics of Include do not really apply when working with in-memory objects).

    If that isn't enough, and your fake implementation of IObjectSet<T> needs some kind of signal on what the Include path is, you can apply a similar trick to the one I used for ObjectQuery<T>. As an example, let's assume that your fake contains an IncludePath property that you need to set. Then you can do it inside the Include extension method like this:

        public static class ObjectQueryExtensions

        {

            public static IQueryable<T> Include<T>(this IQueryable<T> source, string path)

            {

                var objectQuery = source as ObjectQuery<T>;

                if (objectQuery != null)

                {

                    return objectQuery.Include(path);

                }

     

                var fakeObjectSet = source as FakeObjectSet<T>;

                if (fakeObjectSet != null)

                {

                    fakeObjectSet.IncludePath = path;

                }

                return source;

            }

        }

    As you can see in the code, this will only work if you apply Include as the first method on the IObjectSet. Otherwise, you will probably get just a LINQ to Objects IQueryable<T> (if that is what your fakes produce), which doesn't have the IncludePath property.

    On the other side, if you wanted to use a solution based on lazy loading, the good news is that we have adeded a WCF DataContractResolver to Entity Framework that you can use in your WCF services so that POCO proxy instances are serialized as the base POCO class. This was not in beta 1, but will be in the next publically available version.

    Hope this helps,
    Diego




    This posting is provided "AS IS" with no warranties, and confers no rights.
    Monday, July 20, 2009 4:55 AM
    Moderator

All replies

  • Hello,

    There a few things I know you can do. The solution I like the most is to define an Include extension method on IQueryable<T>:

        public static class ObjectQueryExtensions

        {

            public static IQueryable<T> Include<T>(this IQueryable<T> source, string path)

            {

                var objectQuery = source as ObjectQuery<T>;

                if (objectQuery != null)

                {

                    return objectQuery.Include(path);

                }

                return source;

            }

        }


    Notice that this extension method is defined on IQueryable<T>, and not on IObjectSet<T>, however, you can use it on any IObjectSet<T>, since IObjectSet<T> derives from IQueryable<T>. The reason I defined it on IQueryable<T> is that it is more useful there, since ObjectQuery<T>, which does not implement the full IObjectSet<T> interface, also has the capability to do eager loading. Also, the truth is that as soon as you apply any query operation to an ObjectSet<T>, you get an ObjectQuery instance, and you are no longer talking to an ObjectSet.

    Notice that this extension method will only have an effect when applied to an actual Entity Framework query object (either ObjectQuery<T> or ObjectSet<T>). The assumption is that you have control of the test data sets that you use in your unit tests and that you can always make sure that when you are running in-memory queries, your test-doubles will provide the whole graph (the semantics of Include do not really apply when working with in-memory objects).

    If that isn't enough, and your fake implementation of IObjectSet<T> needs some kind of signal on what the Include path is, you can apply a similar trick to the one I used for ObjectQuery<T>. As an example, let's assume that your fake contains an IncludePath property that you need to set. Then you can do it inside the Include extension method like this:

        public static class ObjectQueryExtensions

        {

            public static IQueryable<T> Include<T>(this IQueryable<T> source, string path)

            {

                var objectQuery = source as ObjectQuery<T>;

                if (objectQuery != null)

                {

                    return objectQuery.Include(path);

                }

     

                var fakeObjectSet = source as FakeObjectSet<T>;

                if (fakeObjectSet != null)

                {

                    fakeObjectSet.IncludePath = path;

                }

                return source;

            }

        }

    As you can see in the code, this will only work if you apply Include as the first method on the IObjectSet. Otherwise, you will probably get just a LINQ to Objects IQueryable<T> (if that is what your fakes produce), which doesn't have the IncludePath property.

    On the other side, if you wanted to use a solution based on lazy loading, the good news is that we have adeded a WCF DataContractResolver to Entity Framework that you can use in your WCF services so that POCO proxy instances are serialized as the base POCO class. This was not in beta 1, but will be in the next publically available version.

    Hope this helps,
    Diego




    This posting is provided "AS IS" with no warranties, and confers no rights.
    Monday, July 20, 2009 4:55 AM
    Moderator
  • I´ve used this solution for a bit, and it worked great. Until I decided to have the possiblity to have compiled queries in my repositories. The ILibrary Context has IObjectSet<Book> for ex.

    In my repo I have planned to have a method that returns an expression for later compiling and caching:

     

     

    private Expression<Func<T, IEnumerable<Book>>> CreateGetBooksExpression<T>() where T : ILibraryContext 
    {
    return (T c) => c.Items.OfType<Book>().Include("Tags");
    }

     

     

     

    But the Include is an extetion on IObjectSet<T> so it will not complie in a compliedquery.
    One solution would be to have the query defined twince one for the concrete class and on for the contract(ILibraryContext). But the whole point was to just has to type the query once and be able to test it.

    Any hints on a soultion for this ?

    Tuesday, September 22, 2009 7:33 PM