Why is EntityCollection generated?

Posts   
 
    
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 26-Oct-2006 04:20:04   

EntityCollection and EntityCollection<T> are generated as part of the Adapter templates. Why? They appear to have no dependencies on the classes that are unique to the generated code.

I can see that it provides an opportunity for ppl to extend the collection's functionality. At the same time it prevents anyone from writing generic 'helper-style' libraries that make use of (create instances of) EntityCollection.

EntityCollectionBase2 is abstract with no abstract members (and for some reason has public constructors - to comply with serialization pattern maybe?).

DataAccessAdapter has methods that take IEntityCollection2 as an argument but I cant just create and pass in an EntityCollectionBase2 because it's abstract :|

Does DataAccessAdapter really need to be generated either? I can see the way it's written currently requires it to be extended, but it seems that a stand alone version is feasible running from the ORM support classes assembly.

I'm just trying to write some general methods that my code can use from project to project and I keep having to create these objects and pass them in (or use generics). disappointed

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 26-Oct-2006 10:01:55   

The reason is solely that the class can be extended by partial classes etc.

It was placed in the runtime lib during the beta of v2 but too many people asked to get it moved to the generated code, also because it gave problems with migrating 1.0.2005.x code to v2.0 (databinding forms etc.)

Frans Bouma | Lead developer LLBLGen Pro
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 26-Oct-2006 14:14:07   

Ok. Perhaps you could consider making EntityCollectionBase2 non abstract? It wouldn't break anything would it?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 26-Oct-2006 15:10:29   

No, as that wouldn't make sense, as it's not meant to be created as a separate instance. You should use the non-generic EntityCollection class if you can't instantiate a generic version because you don't know the type.

That's why the non-generic EntityCollection class is also provided, so you can create an entity collection in code without having to revert to the generic version. simple_smile

Or am I not grasping your problem? (which could be the case)

Frans Bouma | Lead developer LLBLGen Pro
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 27-Oct-2006 05:42:59   

What I am trying to do is create a reusable assembly too take some of the repetitive code out of my app. It's mostly been successful simple_smile One thing that prompted me to create this thread is trying to create a reusable static method I can call to retrieve collections of entities.

This is what I had to do, which I dont like much because I have to pass type EntityCollection to the method. The reason I have to do this is (based on my rudimentary understanding) I either have a to create an EntityCollection and pass in an instance of the entity factory OR i can create an instance of the generic EntityCollection which somehow works out what entityfactory to use I guess.


public static EntityCollectionBase2<T> FetchCollection<T, E>(DataAccessAdapterBase adapter, int entityType, IRelationPredicateBucket filter, ISortExpression sorter, params IPrefetchPathElement2[] prefetchPaths) where T : EntityBase2 where E : EntityCollectionBase2<T>, new()
        {
            EntityCollectionBase2<T> collection = new E();//Frans see here - problem line
            IPrefetchPath2 prefetchPath = new PrefetchPath2((int)entityType);

            foreach (IPrefetchPathElement2 pre in prefetchPaths)
                prefetchPath.Add(pre);

            if (adapter == null)
                using (adapter = AdapterRequired())
                {
                    adapter.FetchEntityCollection(collection, filter, 50, sorter, prefetchPath);
                }
            else
                adapter.FetchEntityCollection(collection, filter, 50, sorter, prefetchPath);

            return collection;
        }

Example of use: (this stuff is generated by my front end templates)


RelationPredicateBucket filter = new RelationPredicateBucket();
        IPredicateExpression expression = new PredicateExpression()
        .Add(QuoteFields.ClientId == key);

        filter.PredicateExpression.Add(expression);
        
        SortExpression sorter = new SortExpression();
        sorter.Add(QuoteFields.UpdatedOn | SortOperator.Descending);
        
        return (IList)FetchCollection<QuoteEntity, EntityCollection<QuoteEntity>>(null, EntityType.QuoteEntity, filter, sorter, QuoteEntity.PrefetchPathClient, QuoteEntity.PrefetchPathClo,QuoteEntity.PrefetchPathFinanceAddress, QuoteEntity.PrefetchPathLeadType, QuoteEntity.PrefetchPathPolicyStatus, QuoteEntity.PrefetchPathRiskAddress);

Frans see here I have to let the method know the type of EntityCollection, alternative is I could pass in the type of the EntityFactory. The point is I dont want to do either.

You should use the non-generic EntityCollection class if you can't instantiate a generic version because you don't know the type.

I know the type... I dont know the factory and I dont know the type of the EntityCollection.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 27-Oct-2006 12:49:49   

worldspawn wrote:

What I am trying to do is create a reusable assembly too take some of the repetitive code out of my app. It's mostly been successful simple_smile One thing that prompted me to create this thread is trying to create a reusable static method I can call to retrieve collections of entities.

This is what I had to do, which I dont like much because I have to pass type EntityCollection to the method. The reason I have to do this is (based on my rudimentary understanding) I either have a to create an EntityCollection and pass in an instance of the entity factory OR i can create an instance of the generic EntityCollection which somehow works out what entityfactory to use I guess.


public static EntityCollectionBase2<T> FetchCollection<T, E>(DataAccessAdapterBase adapter, int entityType, IRelationPredicateBucket filter, ISortExpression sorter, params IPrefetchPathElement2[] prefetchPaths) where T : EntityBase2 where E : EntityCollectionBase2<T>, new()
        {
            EntityCollectionBase2<T> collection = new E();//Frans see here - problem line
            IPrefetchPath2 prefetchPath = new PrefetchPath2((int)entityType);

            foreach (IPrefetchPathElement2 pre in prefetchPaths)
                prefetchPath.Add(pre);

            if (adapter == null)
                using (adapter = AdapterRequired())
                {
                    adapter.FetchEntityCollection(collection, filter, 50, sorter, prefetchPath);
                }
            else
                adapter.FetchEntityCollection(collection, filter, 50, sorter, prefetchPath);

            return collection;
        }

Ok, but why not do:

public static EntityCollection<T> FetchCollection<T, E>(IDataAccessAdapter adapter, int entityType, IRelationPredicateBucket filter,
    ISortExpression sorter, params IPrefetchPathElement2[] prefetchPaths) 
        where T : EntityBase2, IEntity2
{
    EntityCollection<T> collection = new EntityCollection<T>(); // factory is determined for you.
    IPrefetchPath2 prefetchPath = new PrefetchPath2((int)entityType);

    foreach (IPrefetchPathElement2 pre in prefetchPaths)
        prefetchPath.Add(pre);

    if (adapter == null)
        using (adapter = AdapterRequired())
        {
            adapter.FetchEntityCollection(collection, filter, 50, sorter, prefetchPath);
        }
    else
        adapter.FetchEntityCollection(collection, filter, 50, sorter, prefetchPath);

    return collection;
}

The factory will be determined for you. (since september or so, the way to do it was found after release).

Example of use: (this stuff is generated by my front end templates)


RelationPredicateBucket filter = new RelationPredicateBucket();
        IPredicateExpression expression = new PredicateExpression()
        .Add(QuoteFields.ClientId == key);

        filter.PredicateExpression.Add(expression);
        
        SortExpression sorter = new SortExpression();
        sorter.Add(QuoteFields.UpdatedOn | SortOperator.Descending);
        
        return (IList)FetchCollection<QuoteEntity, EntityCollection<QuoteEntity>>(null, EntityType.QuoteEntity, filter, sorter, QuoteEntity.PrefetchPathClient, QuoteEntity.PrefetchPathClo,QuoteEntity.PrefetchPathFinanceAddress, QuoteEntity.PrefetchPathLeadType, QuoteEntity.PrefetchPathPolicyStatus, QuoteEntity.PrefetchPathRiskAddress);

which then will become: (cleaned it up a bit)

RelationPredicateBucket filter = new RelationPredicateBucket(QuoteFields.ClientId == key);
SortExpression sorter = new SortExpression(QuoteFields.UpdatedOn | SortOperator.Descending);
return (IEntityCollection2)FetchCollection<QuoteEntity>(null, EntityType.QuoteEntity, filter, sorter, 
    QuoteEntity.PrefetchPathClient, QuoteEntity.PrefetchPathClo,QuoteEntity.PrefetchPathFinanceAddress, 
    QuoteEntity.PrefetchPathLeadType, QuoteEntity.PrefetchPathPolicyStatus, QuoteEntity.PrefetchPathRiskAddress);

This uses IEntityCollection2 instead of IList, as IEntityCollection2 is the interface you should use in code where you don't know the generic type.

Frans Bouma | Lead developer LLBLGen Pro
worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 30-Oct-2006 13:59:07   

Hi Frans,

What you've suggested is exactly what I want to achieve. But I can't due to certain restrictions I'm placing on the assembly dependencies I want to have.

What I want is something like this:

3 Project Assemblies (well 1 is a web project)

FooWeb Foo.BL Foo.DAL (i've fiddled with ur presets to merge the two dal projects)

1 General, reusable from project to project assembly Foo.General

FooWeb references Foo.BL & Foo.DAL Foo.BL reference Foo.DAL

Foo.General references ORMSupportClasses assembly and does not reference any project assemblies.

Because Foo.General does not reference any project assemblies (namely Foo.DAL) it cannot resolve type Foo.DAL.HelperClasses.EntityCollection<T>

Foo.General has the method you wrote in a class called Utils

So in Foo.General.Utils.FetchCollection is the line


    EntityCollection<T> collection = new EntityCollection<T>(); // factory is determined for 

EntityCollection<T> is not defined in the ORMSupportClasses assembly. It's defined in the generated Foo.DAL assembly which is not referenced by Foo.General and therefore can not be resolved when compiling Foo.General. It's abstract base class however can ( as it's defined in ORMSupportClasses). Which is why in my example code I am passing in the type of <E> where E : EntityCollectionBase<T>, new().

Therefore, I put it too you there is measurable value in making EntityCollectionBase an instantiatable class to aid in code reuse/code reduction... unless you have a better idea.

Thanks for clean up of the calling code too. I'll put that in. simple_smile

Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 30-Oct-2006 17:24:11   

Hi,

if EntityCollectionBase is to remain abstract, then you can still add you own concrete dummy implementation of it to your General class lib, based on what is generated now, and keep returning an EntityCollectionBase since the factory auto assigning feature is located in EntityCollectionBase, so you should be fine coming up with a solution that references only the support classes.

Cheers

worldspawn avatar
worldspawn
User
Posts: 321
Joined: 26-Aug-2006
# Posted on: 30-Oct-2006 23:02:31   

....now why didn't I think of that? Thats a great idea!

Thanks smile