Extending LLBLGenPro's ResultsetFields to work like List
These days I had a need to use ResultsetFields for fetching dynamic list. As you know, ResultsetFields derives from EntityFields2 (I am working with LLBLGenPro 2.0 .net 2) which is basically an array. There is nothing wrong with that, it is just you have to set its size beforehand (ok, you can explicitly expand or contract it later on) and you have to use indexes. Something like this:
ResultsetFields fields = new ResultsetFields(5);
fields[0] = CountryEntity.Name;
fields[1] = CountryEntity.Currency;
...
Again, nothing wrong with that, but why would one need to deal with indexes and sizes? Hence, here is my solution:
RhResultsetFields fields = new RhResultsetFields();
fields.Add(CountryEntity.Name);
fields.Add(CountryEntity.Currency);
...
fields.Contract();
See - no size, no indexes. The only thing added is the last line which contracts the array to the proper size and you have to call before using RhResultsetFields instance. This is necessary because I allocate a default size (10 - if not explicitly specified otherwise) at the instance creation and then expand it as necessary for a default size (5).
RhResultsetFields inherits from ResultsetFields and is only a thin wrapper around the base class - it adds dynamic expansion through many overloaded methods (generated out of DefineField methods) and that's it. Here is the code:
#if CF
[SD.LLBLGen.Pro.ORMSupportClasses.Serializable]
#else
[Serializable]
#endif
public class RhResultsetFields : ResultsetFields
{
private int lastFieldIndex = 0;
/// <summary>
/// Initializes a new instance of the RhResultsetFields class.
/// </summary>
public RhResultsetFields()
: this(10)
{
}
/// <summary>Deserialization constructor</summary>
/// <param name="info">Info.</param>
/// <param name="context">Context.</param>
protected RhResultsetFields(SerializationInfo info, StreamingContext context): base(info, context)
{
lastFieldIndex = (int)info.GetInt32("lastFieldIndex");
}
/// <summary>
/// Initializes a new instance of the RhResultsetFields class.
/// </summary>
public RhResultsetFields(int amount): base(amount)
{
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
Contract();
base.GetObjectData(info, context);
info.AddValue("lastFieldIndex", lastFieldIndex);
}
public void Close()
{
base.Contract();
}
private int GetLastIndex()
{
lastFieldIndex++;
if (lastFieldIndex >= base.Count)
Expand(5);
return lastFieldIndex;
}
public int Add(IEntityField2 field)
{
base[GetLastIndex() - 1] = field;
return lastFieldIndex-1;
}
public int Add(IEntityField2 fieldToAdd, AggregateFunction aggregateFunctionToApply)
{
base.DefineField(fieldToAdd, GetLastIndex()-1, aggregateFunctionToApply);
return lastFieldIndex - 1;
}
public int Add(IEntityField2 fieldToAdd, string alias)
{
base.DefineField(fieldToAdd, GetLastIndex() - 1, alias);
return lastFieldIndex - 1;
}
public int Add(IEntityField2 fieldToAdd, string alias, AggregateFunction aggregateFunctionToApply)
{
base.DefineField(fieldToAdd, GetLastIndex() - 1, alias, aggregateFunctionToApply);
return lastFieldIndex - 1;
}
public int Add(IEntityField2 fieldToAdd, string alias, string entityAlias)
{
base.DefineField(fieldToAdd, GetLastIndex() - 1, alias, entityAlias);
return lastFieldIndex - 1;
}
public int Add(IEntityField2 fieldToAdd, string alias, string entityAlias, AggregateFunction aggregateFunctionToApply)
{
base.DefineField(fieldToAdd, GetLastIndex() - 1, alias, entityAlias, aggregateFunctionToApply);
return lastFieldIndex - 1;
}
}
There is one catch though. The original out of the box ResultsetFields doesn't define GetObjectData method as virtual. The method in question is required when deserializing an instance. Thus you have two options:
- Don't include this method at all. Your class won't be able to deserialize but otherwise you won't have problems.
- Modify the template that generates ResultsetFields class (yes, it is generated by LLBLGenPro generator). In my case (.net 2.0, C#, using adapters) this is resultsetFieldsAdapter.template file (located in .net2.x/c# template subfolder): just add keyword virtual to GetObjectData method - that's all.

Here you have it. You are free to use it and modify it. If you add interesting features I would be interested to see them :-)