dynamic collection creation based on collection name in database

Posts   
 
    
Posts: 35
Joined: 22-May-2006
# Posted on: 22-May-2006 22:44:02   

Hi,

I am hoping you might have some insight to the following:

The scenario is as follows. There is a database table that contains a listing of production tables and an associated snapshot table. Right now the application loops through the table rows and for each record builds a datatable based off of the records in the compare1 table field and builds another datatable based on the compare 2 field. The values in the two fields are, e.g. tblBooks & tblBooksOld

What we want to do is the same logic but make use of LLBLGEN. So, I want to be able to create a new collection by passing the the collection name which would be stored in the database field like the table name. The reason for this is instead of doing a case statement is that the tables change so there are new collections and the code would have to be updated each time instead of just updating database records.

Any help would be cool.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 23-May-2006 08:31:08   

The reason for this is instead of doing a case statement is that the tables change so there are new collections

Do you mean new tables are generated at runtime, and you want to fetch records from those tables without regenerating the DAL code?

Posts: 35
Joined: 22-May-2006
# Posted on: 23-May-2006 17:45:13   

No, what i mean is the following.

I already have all of my collections and entities built with LLBLGEN and if new tables are added it is assumed that LLBLGEN has been rerun. We have a component were we need to process any number of the tables in the database. Which tables will be processed is only non at runtime. Instead of having a large case statement that would check every table in the database as to which one we are processing. I would simply like to create a collection based on a passed in parameter. An example would be as follows:

table name: fred collection name: fredCollection entity name: fredEntity

the table names to be processed are stored in a control table whoes values are changed often. As we loop through the table I want to be able to pass the value of the table name, in this case fred, to a function which would create the coresponding collection without having to have a huge case statement.

Would like to have something like: private void createCollection(string tableName) {

Collection = new Collection(tableName)

}

instead of: private void createCollection(string tableName) { if (tableName == "fred") {

fredCollection fredColl = new fredCollection; } }

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 24-May-2006 07:35:11   

I guess you might want to use Reflection.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 24-May-2006 10:15:43   

Reflection isn't really needed even simple_smile . Just use the .NET class Activator to produce a new instance of the collection type, by using Type(typename), where typename is constructed from the name passed in.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 35
Joined: 22-May-2006
# Posted on: 26-May-2006 03:38:31   

thanks for all the help. Everything worked great, but I have a further question. My working function is below:


  public DataTable GetDynamicCollectionData(string collectionName)
    {

        // dec vars
        DataTable dtToReturn = new DataTable();
        string nameSpace;

        try
        {
            nameSpace = "RPC.DataLayer.CollectionClasses." + collectionName + "Collection";
            Type tColl = BuildManager.GetType(nameSpace, true);
            object objColl;
            objColl = Activator.CreateInstance(tColl);


            MethodInfo[] mInfo = objColl.GetType().GetMethods();
            MethodInfo aMethodInfo = null;

            for (int i = 0; i < mInfo.Length; i++)
            {
                if (mInfo[i].Name == "GetMultiAsDataTable")
                {
                    aMethodInfo = mInfo[i];
                    break;
                }
            }

            if (aMethodInfo != null)
            {
                object[] parms;
                parms = new object[3];
                parms[0] = (object)null;
                parms[1] = (object)0;
                parms[2] = (object)null;

                object o = (aMethodInfo.Invoke(objColl, parms));
                dtToReturn = (DataTable)o;
            }

        }
        catch (Exception ex)
        {
            throw new Exception("GetDynamicCollectionData::There was an error invoking the collection dynamically::Error:: " + ex.Message, ex);
        }

        return dtToReturn;
    }

Now my problem is that i need to add a filter (right now I am passing null). I found some posts on the site of other people who did the same thing, but when I try the same tactic I get various errors such as RPC.DataLayer.Entity cannot be converted to SD.LLBGEN.PRO.ORM.IEntity2 or System.Reflection.FieldInfo cannot be converted to SD.LLBGEN.PRO.ORM.IFieldCore. I probably have the namespaces messed up since I am writing this from memory at home. The code I have been trying to use (from memory is):



IPredicateExpression filter = new PredicateExpression();
string entityNamespace;
entityNamespace = "RPC.DataLayer.EntityClasses." + EntityName + "Entity";

Type entityType = BuildManager.GetType(entityNamespace,true);

// Instantiate the entity and retrieve the field
IEntity2 entity = (IEntity2)Activator.CreateInstance(entityType);

IEntityFieldCore field = entity.Fields["IndGuid"];

filter.add(new PredicateFieldCompareValue(field,null,ComparisonOperator.Equal,IndGuid)


any help on making the filter work would be sweet. thanks in advance!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 26-May-2006 17:40:00   

You should use IEntity, not IEntity2. IEntity is for selfservicing, and as you use CollectionClasses somewhere, I guess you're using Selfservicing, so use IEntity instead of IEntity2.

Frans Bouma | Lead developer LLBLGen Pro
Ico
User
Posts: 7
Joined: 02-May-2006
# Posted on: 07-Jun-2006 17:17:08   

Hi there i have similar issue. i need to construct RelationCollection out of PredicateExpression and to dump related entities, so i know which entities can be used to create a PredicateExpression.
It is usefull for me to keep in the database the name of the collection, PersonCollection, as it contains the name ot the Entity, PersonEntity. Instantiating PersonCollection allows me to call GetMulti(). I did not find a way to get SomeCollection name from SomeEntity. Following code might be of helpfull for someone.


List<Type> EntityList = new List<Type>();
//The combobox is filled using reflection with the collections in the generated by LLBLGen
EntityCollectionBase entColl = (EntityCollectionBase)Activator.CreateInstance((Type)comboboxFilterTargetEntity.SelectedItem);
Type t = Type.GetType(String.Format("{0}.{1},{2}", "<namespace_path>.EntityClasses", entColl.EntityFactoryToUse.ForEntityName, "<dll name>"));
//This returns all entities related to the one mentioned in entColl.EntityFactoryToUse.ForEntityName
FilterEntity.DumpRelatedEntities(ref EntityList, t);

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 08-Jun-2006 07:56:14   

would you please elaborate more?

Ico
User
Posts: 7
Joined: 02-May-2006
# Posted on: 08-Jun-2006 11:17:33   

Yeap simple_smile it may be little be aside from the subject I am building mechanism that will allow the users of the system to create filters on different entities. In the database I wanted to store the TargetEntity /collection of which entity is to be fetched after GetMulti() call/, then PredicateExpression in separate binary field.

I have done several tasks to make it posible. First found a way to get the related entites of the target Entity. Reflection and Recursion through the Relations of each Entity down to desired level, in my case up to 3. This is the entities on which the given Filter can have conditions(predicates) on.

Second you must be able to iterate throught the Predicates in a PredicateExpression and extract the Entities taking part in a particular PredicateExpression. After that, having the Entities in the PredicateExpression, you iterate through this list and add just the needed Relations in a RelationCollection, that need to be supplied to GetMulti().

And finally we have the PredicateExpression and the RelationCollection we only need to call GetMulti() and this is where the TargetEntity steps in. As i mentioned before I decided to save the type name of the collection in the database like "PersonCollection" and not entity name as "PersonEntity", cause i did not find anywhere in PersonEntitiy mentioned what collection it builds while there is field in PersonCollection what entities it contains. And one more GetMulti is method of "PersonCollection".


public static EntityCollectionBase GetResultRecords(FilterEntity filter)
        {
            IRelationCollection relationBucket = new RelationCollection();
            List<string> EntityNames = new List<string>();

            //Need to add the target Entity first.
            string fullEntityName = filter.TargetEntity; //This must be TargetCollection
            if (!fullEntityName.Contains(","))
            {
                fullEntityName = String.Format("{0},{1}", fullEntityName,"Omegasoft.Omeks.BusinessObjects");
            }
            Type t = Type.GetType(fullEntityName);
            EntityCollectionBase entityColl = (EntityCollectionBase)Activator.CreateInstance(t);
            EntityNames.Add(entityColl.EntityFactoryToUse.ForEntityName); //This is where EntityName is stored in EntityCollection
            BuildEntitiesList(ref EntityNames, (PredicateExpression)filter.FilterExpression);
            EntityNames.AddRange(EntityNames);

            relationBucket.ObeyWeakRelations = true;
            relationBucket.AddRange(GetRelations(EntityNames, JoinHint.Left));

            bool res = BusinessEntityAdapter.GetMulti(entityColl, filter.FilterExpression,0,relationBucket);
            return entityColl;

        }

BusinessEntityAdapter is our class that is intended to add additional Predicates to restrict the returned entities according to the rights of the user in the system.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 09-Jun-2006 10:04:25   

I'd go a different route: as the project already contains all the meta-data, you'd better add a couple of templates which generate code which builds static hashtables for you at runtime so you can easily get the data you need based on the parameters / values available in your query builder.

For example in a little .lpt template you can generate any meta-data you want, in the format you want / need, so that will simplify your code tremendously, and it's always in-sync with the rest of the generated code you use, as you generate it at the same time as the normal generated code simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Ico
User
Posts: 7
Joined: 02-May-2006
# Posted on: 09-Jun-2006 12:54:50   

Yes simple_smile Played a while with the template Studio and "things came where they belong".

Created small template to be added to entitybase.template containing following code:


 #region  Company Auto Generated
            ///<summary>
            ///Returns the default collection type used for fetching current entity type instances
            ///</summary>
            public  static Type DefaultCollectionType
            {
                get
                {
                    return Type.GetType("<[RootNamespace]>.CollectionClasses.<[CurrentEntityName]>Collection");
                }
            }       
            
            ///<summary>
            ///Create fresh instance of DefaultCollectionType type
            ///</summary>
            public static <[CurrentEntityName]>Collection CreateDefaultCollectionInstance()
            {
                return (<[CurrentEntityName]>Collection)Activator.CreateInstance(DefaultCollectionType);
            }
            
        #endregion

Ico
User
Posts: 7
Joined: 02-May-2006
# Posted on: 09-Jun-2006 13:09:50   

Seems I discovered the hot water disappointed . This does just what I need


PersonEntity person = new PersonEntity();
IEntityCollection entColl = person.EntityFactoryToUse.CreateEntityCollection();