Additionally, if you want to persist the saved data, you might want to do that in a transaction? (i.e. roll back persisted rows if something fails).
I'd then do it batch-wise: fetch 1000 rows with the reader, process them, persist the entities, and repeat with the next 1000 (using paging by specifying pagesize = 1000 and pagenumber is 2 etc.
The projector used, DataProjectorToIEntityCollection2, is just a simple class, as it implements an interface which is easy to implement yourself. I have copied the projector code below. You can base your projector onto this code and for example call out to your process logic by passing the entity, and let it handle it from there. (source is from the runtime lib sourcecode you get access to under a modified BSD2 license once you're a licensee). Our linq provider also uses this class in special cases.
/// <summary>
/// Projector engine which projects raw projection result data onto new entities which are added to a single entitycollection.
/// </summary>
/// <remarks>Adapter specific</remarks>
public class DataProjectorToIEntityCollection2 : IEntityDataProjector, IGeneralDataProjector
{
#region Class Member Declarations
private IEntityCollection2 _destination;
private Dictionary<string, PropertyDescriptor> propertyDescriptors;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="DataProjectorToIEntityCollection2"/> class.
/// </summary>
/// <param name="destination">The destination of the data. It's required that the destination collection has a factory set.</param>
public DataProjectorToIEntityCollection2(IEntityCollection2 destination )
{
if( destination == null )
{
throw new ArgumentNullException( "destination", "destination can't be null" );
}
if( destination.EntityFactoryToUse == null )
{
throw new ArgumentException( "destination doesn't have an EntityFactory set.", "destination" );
}
_destination = destination;
IEntity2 newInstance = _destination.EntityFactoryToUse.Create();
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( newInstance );
propertyDescriptors = new Dictionary<string, PropertyDescriptor>( properties.Count );
foreach( PropertyDescriptor descriptor in properties )
{
propertyDescriptors.Add( descriptor.Name, descriptor );
}
}
/// <summary>
/// Adds a new projection result to the container contained into this instance. The container has to be set in the constructor.
/// </summary>
/// <param name="propertyProjectors">List of property projectors used to create the projection result</param>
/// <param name="rawProjectionResult">The raw projection result.</param>
void IEntityDataProjector.AddProjectionResultToContainer( List<IEntityPropertyProjector> propertyProjectors, object[] rawProjectionResult )
{
this.AddProjectionResultToContainer( propertyProjectors, rawProjectionResult );
}
/// <summary>
/// Adds a new projection result to the container contained into this instance. The container has to be set in the constructor.
/// </summary>
/// <param name="valueProjectors">List of value projectors used to create the projection result</param>
/// <param name="rawProjectionResult">The raw projection result.</param>
void IGeneralDataProjector.AddProjectionResultToContainer( List<IDataValueProjector> valueProjectors, object[] rawProjectionResult )
{
this.AddProjectionResultToContainer( valueProjectors, rawProjectionResult );
}
/// <summary>
/// Performs the actual projection
/// </summary>
/// <param name="projectors">List of projectors used to create the projection result</param>
/// <param name="rawProjectionResult">The raw projection result.</param>
private void AddProjectionResultToContainer( IList projectors, object[] rawProjectionResult )
{
IEntity2 newInstance = _destination.EntityFactoryToUse.Create();
bool fieldSet = false;
// set fields. This is done through property descriptors.
for( int i = 0; i < projectors.Count; i++ )
{
IProjector projector = (IProjector)projectors[i];
IEntityField2 field = newInstance.Fields[projector.ProjectedResultName];
object value = rawProjectionResult[i];
if((value != null) && value.Equals(DBNull.Value))
{
value = null;
}
if(field == null)
{
// set via property descriptor
PropertyDescriptor property = null;
if( propertyDescriptors.TryGetValue( projector.ProjectedResultName, out property ) )
{
property.SetValue(newInstance, value);
fieldSet = true;
}
}
else
{
// set through field object.
if( field.IsReadOnly )
{
// just set the value, don't set dirty flags.
field.ForcedCurrentValueWrite(value);
fieldSet = true;
}
else
{
fieldSet = newInstance.SetNewFieldValue(field.FieldIndex, value);
}
}
}
if(!fieldSet && (rawProjectionResult.Length == 1) && rawProjectionResult[0].GetType().IsAssignableFrom(newInstance.GetType()))
{
// special case: m:1/1:1 related entity is added. This ends up in this projection routine because the end result is
// a collection of entities. Typical example: from o in md.Orders select o.Customer;
_destination.Add((IEntity2)rawProjectionResult[0]);
}
else
{
_destination.Add(newInstance);
}
}
}