Reflected FieldIndex for SortClause

Posts   
 
    
cycling321
User
Posts: 11
Joined: 29-Jul-2005
# Posted on: 06-Sep-2005 04:08:35   

I'm trying to dynamically create sort clauses by reading in an XML configuration file.

The XML file is as follows:


        <SortConfiguration>
          <Entity Name="User" Default="LastName">
            <SortType Name="LastName">
              <SortClause FieldName="LastName" SortOperator="Ascending"/>
              <SortClause FieldName="FirstName" SortOperator="Ascending"/>
              <SortClause FieldName="MiddleName" SortOperator="Ascending"/>
            </SortType>
            <SortType Name="LastNameDescending">
              <SortClause FieldName="LastName" SortOperator="Descending"/>
              <SortClause FieldName="FirstName" SortOperator="Descending"/>
              <SortClause FieldName="MiddleName" SortOperator="Descending"/>
            </SortType>
            <SortType Name="Birthdate">
              <SortClause FieldName="Birthdate" SortOperator="Ascending"/>
              <SortClause FieldName="LastName" SortOperator="Ascending"/>
              <SortClause FieldName="FirstName" SortOperator="Ascending"/>
              <SortClause FieldName="MiddleName" SortOperator="Ascending"/>
            </SortType>
            <SortType Name="BirthdateDescending">
              <SortClause FieldName="Birthdate" SortOperator="Ascending"/>
              <SortClause FieldName="LastName" SortOperator="Descending"/>
              <SortClause FieldName="FirstName" SortOperator="Descending"/>
              <SortClause FieldName="MiddleName" SortOperator="Descending"/>
            </SortType>
          </Entity>
        </SortConfiguration>

Of course, there are going to be many entities with many different sort configurations.

I've managed to use reflection to find the correct entity, enum, and selection. My problem is that now I can't take that enum and pass it into the SortClauseFactory to create the proper sort clause. I get a compiler error: "Argument '1': cannot convert from 'object' to 'UserFieldIndex'. I've managed to get this far and now I'm stuck.

Here is the section my code that performs the reflection. It will not compile and you probably have to step through the debugger to see what's going on:


ISortExpression sorter = new SortExpression();

// The field name to sort by
string fieldName = reader.GetAttribute('FieldName');

// Determine the correct sort operator
SortOperator sortOperator;
if (reader.GetAttribute('SortOperator').Equals('Ascending')) {
    sortOperator = SortOperator.Ascending;
}
else {
    sortOperator = SortOperator.Descending;
}

// Use reflection to find the field index enum
Assembly dataAssembly = Assembly.LoadFrom('Data.dll');
Type fieldIndexType = dataAssembly.GetType('Data.' + entityName + 'FieldIndex');
FieldInfo fieldIndexSelection = fieldIndexType.GetField(fieldName);

sorter.Add(SortClauseFactory.Create(Enum.Parse(fieldIndexType, fieldName),
                                    sortOperator));

Note: Double quotes were replaced with single quotes to render correctly in the forum. Looping and control flow elements were removed for clarity.

Any thoughts as to how I can solve this problem?

Should the SortClauseFactory contain a more generalized overload?

Thanks!

cycling321
User
Posts: 11
Joined: 29-Jul-2005
# Posted on: 06-Sep-2005 05:42:50   

After much deliberation and a few slices of pizza, I got it to work. simple_smile

Thanks to this thread: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=688

The IEntityFieldCore was the key. Here's the updated listing for all who are interested:


ISortExpression sorter = new SortExpression();

// The field name to sort by
string fieldName = reader.GetAttribute('FieldName');

// Determine the correct sort operator
SortOperator sortOperator;
if (reader.GetAttribute("SortOperator").Equals("Ascending")) {
    sortOperator = SortOperator.Ascending;
}
else {
    sortOperator = SortOperator.Descending;
}

// Use reflection to find the entity
Assembly dataAssembly = Assembly.LoadFrom("Data.dll");
Type entityType = dataAssembly.GetType("Data." + entityName + "Entity");

// Instantiate the entity and retrieve the field
IEntity2 entity = (IEntity2)Activator.CreateInstance(entityType);
IEntityFieldCore field = entity.Fields[fieldName];

// Add the sort clause
sorter.Add(new SortClause(field, null, sortOperator));

Viola!

Thanks Frans! I'm just blown away at how powerful LLBLGen is. Very, very cool.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 06-Sep-2005 09:38:10   

smile

You also could read in the xml in a static constructor, and keep it in a readonly hashtable (static), so you just need to consult that hashtable to get the proper value (which is faster than the reflection way wink )

Or use a simple template and generate the code out into a helper class. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
cycling321
User
Posts: 11
Joined: 29-Jul-2005
# Posted on: 06-Sep-2005 15:15:52   

Otis wrote:

You also could read in the xml in a static constructor, and keep it in a readonly hashtable (static), so you just need to consult that hashtable to get the proper value (which is faster than the reflection way wink )

Cool, that's in essence what I was doing. I'm currently reading the XML through a private constructor of a singleton class, so I'll only get hit on the configuration once. The results of the XML configuration are placed into a hashtable that is exposed through a GetEntitySorters(string entityName) method.

I've managed to eliminate the reflection code to use the Activator exclusively:


string assemblyName = "Data.dll";
string className = "Data.EntityClasses." + entityName + "Entity";
IEntity2 entity = (IEntity2)Activator.CreateInstanceFrom(assemblyName, className).Unwrap();

This works well too. Thanks simple_smile

Posts: 35
Joined: 22-May-2006
# Posted on: 26-May-2006 16:15:48   

string assemblyName = "Data.dll";
string className = "Data.EntityClasses." + entityName + "Entity";
IEntity2 entity = (IEntity2)Activator.CreateInstanceFrom(assemblyName, className).Unwrap();

How do you get that to work? When I load my DLL (RPC.DataLayer.dll) with the correct classname it will create the entity if i cast it as an object only. If i implement IEntity2 I get an error as follows:

cannot convert RPC.DataLayer.<entity>Entity to SD.SD.LLBLGen.Pro.ORMSupportClasses.IEntity2

do you have any information that may help me?

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

If you're using selfservicing, cast to IEntity instead simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 35
Joined: 22-May-2006
# Posted on: 26-May-2006 19:13:05   

thanks! that worked perfectly.