Repositiry pattern using Linq to LLBLGen

Posts   
 
    
Posts: 16
Joined: 15-Apr-2008
# Posted on: 20-May-2009 00:21:11   

I am trying to implement repository pattern using Linq to LLBLGen. Version 2.6, Adaptor Pattern (I am new to Adaptor pattern, Linq to LLBLGen and to repository pattern simple_smile )

My reporsitory interface is

    public interface IProductsRepository
    {
        IQueryable<ProductEntity> Products { get; }
    }

And this is my repository class:

    public class SqlProductsRepository : IProductsRepository
    {
        public IQueryable<ProductEntity> Products
        {
            get 
            {
                using (DataAccessAdapter adapter = new DataAccessAdapter())
                {
                    LinqMetaData metaData = new LinqMetaData(adapter);
                    var q = from p in metaData.Product
                            select p;
                    return q;
                }

            }
        }
    }

When I call this repository - productsRepository.Products.ToList() I get an error -- "This DataAccessAdapter instance has already been disposed, you can't use it for further persistence activity Object name: 'DataAccessAdapterBase'."

I can undertstand that the adaptor object has expired, which is causing this error. But then whats the proper way?

Can someone guide me about how to return IQueryable and add filters later in the calling function before binding the data to UI.

Thanks!

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-May-2009 05:17:39   

You can build the IQueryable and set the provider later.

public class SqlProductsRepository : IProductsRepository
    {
        public IQueryable<ProductEntity> Products
        {
            get
            {               
                    LinqMetaData metaData = new LinqMetaData();
                    var q = from p in metaData.Product
                            select p;
                    return q;
                }
            }
        }
    }

You now can play with the IQueryable and then setup the Provider and execute the query.

// retrieve the iqueryable
IQueryable<ProductEntity> iqResult = new SqlProductsRepository().Products;

// some filter (if needed). Note that no query is executed yet.
iqResult.Where(c => c.Country == "GT");

// setup and execute the query
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
    // setup the provider
    ((LLBLGenProProvider2)q.Provider).AdapterToUse = adapter;

    // execute (fetch)
    EntityCollection<CustomersEntity> customers = 
        ((ILLBLGenProQuery)q).Execute<EntityCollection<CustomersEntity>>();
}

For more info, read about LLBLGenProProvider2 and ILLBLGenProQuerywink

David Elizondo | LLBLGen Support Team
Posts: 16
Joined: 15-Apr-2008
# Posted on: 20-May-2009 12:04:10   

Thanks! That works really well and takes me one big step further. However, I land up in another confusion. I hope there is an elegant solution for this.

When I use Mock objects or fake repository, I can write calling code like this:

var iqResult = new FakeProductsRepository().Products
            .Where(c => c.Country == "GT")
            .Skip(20)
            .Take(10)
            .ToList();

But I cannot do this using adaptor pattern, right? Is there any elegant way to call the Execute using similar syntax? i.e. a good syntax which can be common between Sql and Fake Repository.

I guess I am asking Adaptor pattern to work like Self Servicing pattern simple_smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39865
Joined: 17-Aug-2003
# Posted on: 20-May-2009 16:39:36   

explorer2309 wrote:

Thanks! That works really well and takes me one big step further. However, I land up in another confusion. I hope there is an elegant solution for this.

When I use Mock objects or fake repository, I can write calling code like this:

var iqResult = new FakeProductsRepository().Products
            .Where(c => c.Country == "GT")
            .Skip(20)
            .Take(10)
            .ToList();

But I cannot do this using adaptor pattern, right?

except for the ToList, you can of course, as you simply append IQueryable extension methods to the query. Though it breaks the repository pattern: you shouldn't append specifications to a result returned from the repository, that's the job of the repository.

Is there any elegant way to call the Execute using similar syntax? i.e. a good syntax which can be common between Sql and Fake Repository. I guess I am asking Adaptor pattern to work like Self Servicing pattern simple_smile

In general, one should use a specification pattern implementation (so something which specifies what you want to retrieve) and pass that to the repository which then returns what you asked for. The way you built it now mitigates the purpose of the repository: you're querying data outside the repository.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 16
Joined: 15-Apr-2008
# Posted on: 21-May-2009 08:51:09   

Okay, I have tried incorporating your suggestions and arrived at this:

The Interface:

    public interface IProductsRepository
    {
        IQueryable<ProductEntity> Products { get; }
        IEnumerable<ProductEntity> RunQuery(IQueryable<ProductEntity> products);
    }

Sql Repository:

    public class SqlProductsRepository : IProductsRepository
    {
        public IQueryable<ProductEntity> Products
        {
            get 
            {
                using (DataAccessAdapter adapter = new DataAccessAdapter())
                {
                    LinqMetaData metaData = new LinqMetaData(adapter);
                    var q = from p in metaData.Product
                            select p;
                    return q;
                }
            }
        }

        public IEnumerable<ProductEntity> RunQuery(IQueryable<ProductEntity> products)
        {
            using (DataAccessAdapter adapter = new DataAccessAdapter())
            {
                // setup the provider
                ((LLBLGenProProvider2)products.Provider).AdapterToUse = adapter;

                // execute (fetch)
                EntityCollection<ProductEntity> retproducts =((ILLBLGenProQuery)products).Execute<EntityCollection<ProductEntity>>();
                return retproducts;
            }
        }
    }

Fake Repository:

    public class FakeProductsRepository : IProductsRepository
    {
        public IQueryable<ProductEntity> Products
        {
            get 
            {
                return new List<ProductEntity>
                {
                    new ProductEntity {Name="Table", Price=264, Category="Furniture"},
                    new ProductEntity {Name="Chair", Price=73, Category="Furniture"},
                    new ProductEntity {Name="Sofa", Price=966, Category="Furniture"}
                }.AsQueryable(); 
            }
        }

        public IEnumerable<ProductEntity> RunQuery(IQueryable<ProductEntity> products)
        {
            return products.ToList();
        }
    }

Calling Code

            //IProductsRepository repository = new FakeProductsRepository(); // for fake repo
            IProductsRepository repository = new SqlProductsRepository();   //  for Sql repo
            var q = repository.Products
                    .Where(c => c.Category == "Furniture")
                    .Skip(20)
                    .Take(10);
            return repository.RunQuery(q);

Does this satisfy the ideal way of using repository pattern with LLBLGen or repository pattern in general?

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 21-May-2009 11:35:13   

Your implementation still has the queries etc. outside the repository. This breaks the reason why they're there: a repository hides how things are queried. For example if the requirement changes to join with another entity and filter on that always, you can do that in 1 place: inside the repository. Now your code has these kind of queries all over the place meaning that the idea of having a repository with a single set of queries is simply not there.

Your repositories are now just very tiny wrappers around adapters.

jakenuts
User
Posts: 10
Joined: 28-Aug-2009
# Posted on: 29-Aug-2009 07:40:22   

Can an IQueryable<ProductEntity> be cast to an IQueryable<IProductEntity> so that I don't need to expose the storage mechanism to users of the repository?

I've tried Cast<> but that seems to have some meaning in Linq/SQL and so an exception is thrown as IProduct isn't an LLBLGen entity. (As a side note, MindScape had the same limitation in their linq classes but removed it after re-reading the linq specification.

Alternately, is there another way to accomplish this?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39865
Joined: 17-Aug-2003
# Posted on: 29-Aug-2009 10:47:06   

'Cast' has a meaning in the scope of a linq query, as it is used to deal with inheritance types in a query ran on the db. I don't see how that can be seen differently. So, if you have metaData.Employee and you want to apply a filter for a subtype to that iqueryable, you can use Cast<ManagerEntity>() on that iqueryable, to signal the query that you want to deal with different types. Casting to an interface has a different meaning: fetch all types implementing that interface. That's a feature not supported by LLBLGen Pro, you can't filter on interfaces.

With repositories, you shouldn't return IQueryables: they're not sets, they're queries with inside them the fetch mechanism. The whole point of a repository is to provide fetched sets of entities, not queries which aren't fetched yet. I.o.w.: if you have to append query operators to elements received from a repository, you should use a specification pattern implementation and pass the specification to the repository which then would append the query elements before returning the elements to you. Why else use repositories?

I don't follow what mindscape did with removing a limitation, as Cast<> is a logical operator in a linq query, and IF the o/r mapper supports filtering on interfaces, it could support that too through Cast<Interface> but as said, we don't support that, so you can't use it that way.

So for you it's not really useful to append Cast directly to the IQueryable and returning an IQueryable, simply because the repository shouldn't be used as a query creation class, but as a base for fetched entities for the aggregate root it manages. I.o.w.: fetching is done inside the repository.

So your methods can return IEnumerable<IProductEntity> and you simply first fetch the data to an entity collection (either by ToList() or by the more efficient Execute method on ILLBLGenProQuery interface on the queryable) and then apply cast to that object.

Frans Bouma | Lead developer LLBLGen Pro
jakenuts
User
Posts: 10
Joined: 28-Aug-2009
# Posted on: 02-Sep-2009 05:49:45   

Thanks, that makes alot of sense.

I have changed course and am wrapping the filtering details inside the specifications, but it seems as though the ability to pass an IQueryable<Interface> would be useful there as well. If I can "cast" (overloaded?) away the specifics of the entities implementation, the specifications I create can be untangled from the storage they filter. Though it might be over-abstraction, the idea that the specifications could be applied to any flavor of storage, but would remain efficient because of the translation to sql in LLBLGens case really appeals. Could be off on another unnecessary jaunt tho.

jakenuts
User
Posts: 10
Joined: 28-Aug-2009
# Posted on: 02-Sep-2009 07:02:33   

I mentioned that Mindscape had changed their implementation after looking again at the spec. I think this is what that refererred to:

When the object returned by Cast is enumerated, it enumerates the source sequence and yields each element cast to type T. An InvalidCastException is thrown if an element in the sequence cannot be cast to type T.

Standard Query Operators - MSDN Linq Site

It seems as though Cast was intended for coercing the output rather than filtering like OfType(). Having said this, I'm in absolute awe of anyone who could write code to make any of this work so if I'm beyond my depth, that's cool too.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39865
Joined: 17-Aug-2003
# Posted on: 03-Sep-2009 10:45:31   

jakenuts wrote:

Thanks, that makes alot of sense.

I have changed course and am wrapping the filtering details inside the specifications, but it seems as though the ability to pass an IQueryable<Interface> would be useful there as well. If I can "cast" (overloaded?) away the specifics of the entities implementation, the specifications I create can be untangled from the storage they filter. Though it might be over-abstraction, the idea that the specifications could be applied to any flavor of storage, but would remain efficient because of the translation to sql in LLBLGens case really appeals. Could be off on another unnecessary jaunt tho.

Every abstraction should solve a complexity problem, not add one wink . The IQueryable<T> interface is tempting, but has a big drawback: it's not a set, so sooner or later you'll run into problems with that as the actual executing code (adapter for example) is moved outside the repository.

Cast<> is indeed needed for coercing, though at the DB level, there's little what you can do other than filtering. So at the db level, most of these type related operators are actually doing the same thing. We decided to filter on the type when using Cast<> as it's the only sane way to use it at the DB level: throwing an exception isn't really helpful, nor will it benefit the scope of where you can use it in (as we don't support interface based filtering). After all, linq is a set of sequences, so all operators (e.g. select) after the Cast<> are only working on the type operand of Cast anyway, and as exception throwing inside a query is not doable, it's the same thing as filtering (IMHO).

Frans Bouma | Lead developer LLBLGen Pro
jakenuts
User
Posts: 10
Joined: 28-Aug-2009
# Posted on: 03-Sep-2009 17:47:45   

Hmmm, I think my attempt to isolate the entity implementation to only the classes that "need to know" probably is a useful idea, partly because it's what is allowing me to evaluate LLBLGen as a replacement for the Lightspeed. It's going fairly easily, but that IQueryable<> thing is presenting a problem. I may be able to just wrap it in some sort of adapater that casts from the concrete type to the interface, I'll post it if I have any success.

Perhaps going forward, since Cast<> doesn't have meaning at the DB level, it can be simplified to do just coercion. I'm sure that's much easier said than done (and deployed), but it would be a nice way to open up the API a bit for people trying to achieve PONO-like domains.

Thanks again!

James

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39865
Joined: 17-Aug-2003
# Posted on: 04-Sep-2009 12:44:05   

jakenuts wrote:

Hmmm, I think my attempt to isolate the entity implementation to only the classes that "need to know" probably is a useful idea, partly because it's what is allowing me to evaluate LLBLGen as a replacement for the Lightspeed. It's going fairly easily, but that IQueryable<> thing is presenting a problem. I may be able to just wrap it in some sort of adapater that casts from the concrete type to the interface, I'll post it if I have any success.

Evans' idea was that repositories should be the ones to know how to fetch elements for an aggregate root, not some code outside the repositories, and exposing an IQueryable is exactly doing what Evans didn't want, so I think it's better (albeit perhaps more code) to keep them inside the repositories.

Perhaps going forward, since Cast<> doesn't have meaning at the DB level, it can be simplified to do just coercion. I'm sure that's much easier said than done (and deployed), but it would be a nice way to open up the API a bit for people trying to achieve PONO-like domains.

Though that would bring a bit of a problem, as our entities aren't POCO's, so even if you could cast the aggregate root entities (e.g. Customer) to ICustomer, the problem comes when you want to read its Orders... (Customer.Orders).

Frans Bouma | Lead developer LLBLGen Pro
jakenuts
User
Posts: 10
Joined: 28-Aug-2009
# Posted on: 04-Sep-2009 17:47:14   

Yup, I'm still new to 'repository' and as I've seen some real struggles with it's implementation (Ayendes nearly scared me off the idea). Separating filtering logic into specifications seems to be the best way to go especially as they can be combined, etc. The issue I'm having is that if they resided in my domain model, they all reside together whether they filter LLBLGen entities or simple objects decoupling them from the storage.

I'm hoping to use a nice implementation of Specification I found in NCommon (http://code.google.com/p/ncommon/) and gathering all the various filters in the domain model itself. To do so, I've got to provide IQueryable<Interface> or create alot of redundant interfaces for the specifications and their factories. It's a shame because I'm really growing fond of using interfaces to both hide entity implementation and to allow subsets of fields to be passed upward hiding different sorts of entities.

No worries though, after this "architectural space walk" is completed, I'm sure I'll be happy to get back to solid ground.

Rohland
User
Posts: 14
Joined: 25-Jul-2007
# Posted on: 24-Jan-2010 20:24:51   

Sorry to dig up an old post, but I finally got round to posting a blog article on how I implemented the Repository Pattern using LLBLGEN Pro. I thought it would be useful to post the link here to support others wanting to do the same thing. Check out the post here: http://www.rohland.co.za/index.php/2010/01/23/implementing-the-repository-pattern-with-llblgen/

Any suggestions or comments are welcome.

Cheers, Rohland

Edit: I realise that in my post I don't chat about the actual repositories themselves but rather the entity types a relevant repository can return. The important thing to note is that by ensuring your repository only returns interfaces exposing entity properties, you are not in danger of breaking the pattern since no property of an entity could invoke a query outside the repository.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 24-Jan-2010 21:43:07   

Thanks for sharing your post Rohland wink

David Elizondo | LLBLGen Support Team