Create collection from Type

Posts   
 
    
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 12-Apr-2005 22:54:09   

Hi,

If I have an object of type 'Type' which describes a class which implements 'IEntity2', what is the best way to retrieve a collection of these objects which implement 'IEntity2'?

Or in other words, how would I complete the following code....


Type type;
IEntityCollection2 collection = new ??
da.FetchEntityCollection(collection, null);

I believe I need the appropriate 'EntityCollection' and factory object.

Cheers,

Ian.

simple_smile

Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 13-Apr-2005 00:05:02   

At the moment my idea is to add a method to the IEntity2 interface.


IEntityCollection2 GetEntityCollection();

And then have a custom template implement the method such that it returns the required type. Thus for a CinemaEntity....


public override IEntityCollection2 GetEntityCollection()
{
  return new EntityCollection(new CinemaEntityFactory());
}

Then I can go like this...


Type type;
IEntity2 entityInstance = (IEntity2)Activator.CreateInstance(objectType);
IEntityCollection2 collection = entityInstance.GetEntityCollection();
da.FetchEntityCollection(collection, null);

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 13-Apr-2005 10:24:27   

I believe you need the right factory. The collection is a generic class.

One way to do that is to generate a static class which does this for you. I think that's the easiest way to achieve this, and the most maintainable one, as you don't have to alter anything.

Something like:


public class EntityFactoryRetriever
{
    /// <summary>
    /// No instances allowed.
    /// </summary>
    private EntityFactoryRetriever()
    {
    }


    /// <summary>
    /// Tries to retrieve the entity factory belonging to the entity passed in.
    /// </summary>
    /// <param name="entity">the entity for which the factory has to be returned</param>
    /// <returns>ready to use factory instance for the entity passed in or null if not found</returns>
    public static IEntityFactory2 GetEntityFactory(IEntity2 entity)
    {
        IEntityFactory2 toReturn = null;
        switch(entity.GetType().UnderlyingSystemType.FullName)
        {
<[Foreach Entity CrLf]>         case "<[RootNamespace]>.EntityClasses.<[CurrentEntityName]>Entity":
                toReturn = new <[CurrentEntityName]>EntityFactory();
                break;<[NextForeach]>
            default:
                // nothing, already set to null
                break;
        }

        return toReturn;
    }
}

Bind this to a templateID you define, add a task to a copy of the generator config you want to use, to generate the template and generate code. You'll now have this class in your generated code. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 13-Apr-2005 13:31:13   

I'm not sure this is quite going to work. You see my data access code is programming against the support classes. The assembly only contains a reference to 'SD.LLBLGen.Pro.ORMSupportClasses'.

So given just an object of type IEntity2 I somehow need to get a reference to the appropriate version of 'EntityFactoryRetriever' and 'EntityCollection' and I don't see how this is possible without modifying 'IEntity2'.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 13-Apr-2005 13:55:00   

Activator is not able to instantiate a type if the assembly isn't in the appDomain, so the assembly which contains the factories has to be loaded/referenced.

You can also modify the example template I gave to return the full type name. You can then use that type name with assembly load and activator to instantiate the proper instance using that type name. Modifying IEntity2 is not recommended.

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 13-Apr-2005 14:54:51   

Otis wrote:

Activator is not able to instantiate a type if the assembly isn't in the appDomain, so the assembly which contains the factories has to be loaded/referenced.

Yes, the assembly containing the generated types is referenced by the final project but not by the data access code. This is in a seperate assembly which references the support classes and a UIMapper.

The data access code is within a class which interfaces between a UIMapper and LLBLGen. If I have this assembly reference the generated classes then I'm tying the UIMapper and data access code to a specific set of entities.

But yeah, I think I see what you mean. Just use the string name on the entity instance to create the right factory.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 13-Apr-2005 20:56:53   

Ian wrote:

Otis wrote:

Activator is not able to instantiate a type if the assembly isn't in the appDomain, so the assembly which contains the factories has to be loaded/referenced.

Yes, the assembly containing the generated types is referenced by the final project but not by the data access code. This is in a seperate assembly which references the support classes and a UIMapper.

The data access code is within a class which interfaces between a UIMapper and LLBLGen. If I have this assembly reference the generated classes then I'm tying the UIMapper and data access code to a specific set of entities.

Then I see your point indeed. The string approach (return the full type as a string) with the class I mentioned above is the most easiest I think.

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 14-Apr-2005 15:59:24   

This is what I've come up with...


namespace LLBLGenExtensions
{
    public interface IEntityHelper
    {
        IEntityCollection2 GetEntityCollection(Type type) ;
    }
}

And then...


using System;
using System.Collections;

using <[RootNamespace]>.EntityClasses;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.FactoryClasses;

using SD.LLBLGen.Pro.ORMSupportClasses;
using LLBLGenExtensions;


public class EntityHelper : IEntityHelper
{
    private delegate IEntityFactory2 FactoryRetriever();
    private Hashtable hashtable = new Hashtable();

    public EntityHelper()
    {
<[Foreach Entity CrLf]>     hashtable.Add(typeof(<[CurrentEntityName]>Entity), new FactoryRetriever(Get<[CurrentEntityName]>EntityFactory));<[NextForeach]>
    }

    public IEntityCollection2 GetEntityCollection(Type type) 
    {
        FactoryRetriever retriever1 = (FactoryRetriever)hashtable[type];
        return new EntityCollection(retriever1());
    }
<[Foreach Entity CrLf]>
    private IEntityFactory2 Get<[CurrentEntityName]>EntityFactory()
    {
        return new <[CurrentEntityName]>EntityFactory();
    }<[NextForeach]>

}

Followed by...


Type type1 = typeof(CinemaEntity);

IEntityHelper helper = new EntityHelper();
IEntityCollection2 collection2 = helper.GetEntityCollection(type1);

What do you think?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 14-Apr-2005 17:03:03   

looks great! Thanks for sharing this, Ian! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 14-Apr-2005 19:30:45   

Here's the final version. Perhaps something like this functionality could find its way into a later version of LLBLGen.


using System;
using System.Collections;
using SD.LLBLGen.Pro.ORMSupportClasses;

namespace LLBLGenExtensions
{
    public interface IEntityHelper
    {
        IEntityCollection2 GetEntityCollection(Type type);
        IEntity2 GetEntityInstance(Type type);
        IPrefetchPath2 GetEntityPreFetchPath(Type type, string viewName);
    }

    public abstract class EntityHelperBase : IEntityHelper
    {   
        protected class PrefetchPathData
        {
            private int entityType;
            public int EntityType
            {
                get { return entityType; }
                set { entityType = value; }
            }

            private IPrefetchPathElement2 [] pathElements;
            public IPrefetchPathElement2[] PathElements
            {
                get { return pathElements; }
                set { pathElements = value; }
            }
        }

        protected delegate IEntityFactory2 FactoryRetriever();
        protected Hashtable htFactories = new Hashtable();
        private Hashtable htPrefetch = new Hashtable();
        
        public abstract IEntityCollection2 GetEntityCollection(Type type);
        protected abstract void CreateFactoryHashtable();
        protected abstract void CreatePreFetchPaths();

        public EntityHelperBase()
        {
            CreateFactoryHashtable();
            CreatePreFetchPaths();
        }

        protected void AddPrefetchData(Type type, int entityType, string viewName, params IPrefetchPathElement2 [] pathElements)
        {
            PrefetchPathData pathData = new PrefetchPathData();
            pathData.EntityType = entityType;
            pathData.PathElements = pathElements;

            htPrefetch.Add(MakeKey(type,  viewName), pathData);
        }

        protected static string MakeKey(Type type, string key)
        {
            return string.Concat(type.ToString(), key);
        }

        public IPrefetchPath2 GetEntityPreFetchPath(Type type, string key)
        {
            PrefetchPathData pathData = (PrefetchPathData)htPrefetch[MakeKey(type, key)];
            
            if (pathData == null)
            {
                return null;
            }

            IPrefetchPath2 prefetchPath = new PrefetchPath2(pathData.EntityType);

            foreach(IPrefetchPathElement2 path in pathData.PathElements)
            {
                prefetchPath.Add(path);
            }

            return prefetchPath;
        }

        public IEntity2 GetEntityInstance(Type type)
        {
            return GetFactory(type).Create();
        }

        protected IEntityFactory2 GetFactory(Type type)
        {
            FactoryRetriever retriever1 = (FactoryRetriever)htFactories[type];
            return retriever1();
        }
    }
}



Template:


using System;

using <[RootNamespace]>.EntityClasses;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.FactoryClasses;

using SD.LLBLGen.Pro.ORMSupportClasses;
using LLBLGenExtensions;

namespace <[RootNamespace]>
{
    public class EntityHelper : EntityHelperBase
    {
        protected override void CreatePreFetchPaths()
        {
            //e.g. AddPrefetchData(typeof(OrderEntity), (int)EntityType.OrderEntity, "WithCustomer",  OrderEntity.PrefetchPathCustomer);
            #region Custom User Code

            #endregion
        }

        public override IEntityCollection2 GetEntityCollection(Type type) 
        {
            return new EntityCollection(GetFactory(type));
        }
        
        protected override void CreateFactoryHashtable()
        {
<[Foreach Entity CrLf]>         htFactories.Add(typeof(<[CurrentEntityName]>Entity), new FactoryRetriever(Get<[CurrentEntityName]>EntityFactory));<[NextForeach]>
        }
    <[Foreach Entity CrLf]>
        private IEntityFactory2 Get<[CurrentEntityName]>EntityFactory()
        {
            return new <[CurrentEntityName]>EntityFactory();
        }<[NextForeach]>
    }
}

Example:


Type objectType = typeof(OrderEntity);
IEntityHelper LLBLGenHelper = new EntityHelper();

IEntity2 entityInstance = LLBLGenHelper.GetEntityInstance(objectType);

DataAccessAdapter da = new DataAccessAdapter();
da.FetchEntity(entityInstance, LLBLGenHelper.GetEntityPreFetchPath(objectType, "WithCustomer"));

IEntityCollection2 collection = LLBLGenHelper.GetEntityCollection(objectType);