- Home
- LLBLGen Pro
- Bugs & Issues
Projection onto EntityCollection leaves DbValue null
Joined: 02-Mar-2005
v2.6.08.1013
I am using a stored proc projection to load various EntityCollections (similarly to . The problem is that although the CurrentValue of the Entity fields are set properly, the DbValue is not set (still null), which causes plenty of problems for editing down the road in UI.
Can this be corrected? Or is there a reasonable explanation that I'm missing?
Thanks, Josh
Joined: 17-Aug-2003
tomahawk wrote:
v2.6.08.1013
I am using a stored proc projection to load various EntityCollections (similarly to . The problem is that although the CurrentValue of the Entity fields are set properly, the DbValue is not set (still null), which causes plenty of problems for editing down the road in UI.
Can this be corrected? Or is there a reasonable explanation that I'm missing?
Thanks, Josh
The projector to entitycollection is a class which works but it isn't a class which has the most optimized codepath.
There are a couple of things with this projector. Please open the DataProjectorToEntityCollection.cs file in the runtime lib and look at the AddProjectionResultToContainer method for understanding: (2 classes are in that file, one for ss and one for adapter) - it creates new entities and sets field values through the SetNewFieldValue method. this method makes the field become changed. - It's also not the fastest way to set values in an entity through projection though it's the one which always works and always touches the validators for example. It uses SetNewFieldValue, which is slower than the direct ForcedCurrentValueWrite(value, value)
So it has some disadvantages in its current state for some situations, like the dirty writes. However, in other situations, like projecting entities to new entities from a collection/view, it is exactly what you want (new entities, with dirty fields and no DbValue set)
I.o.w., there's no single routine to get all situations right. I admit it should have been an option in the projector itself, but alas, that's not the case. I'll make that change in v3.
What you could do is the following. Below I've copied the projector's sourcecode and have altered the code so it will produce a new entity which isn't dirty and which has DBValue set as well. Use an instance of THIS projector for the proc projections.
/// <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 DataProjectorToIEntityCollection2Custom : IEntityDataProjector, IGeneralDataProjector
{
#region Class Member Declarations
private IEntityCollection2 _destination;
private Dictionary<string, PropertyDescriptor> propertyDescriptors;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="DataProjectorToIEntityCollection2Custom"/> class.
/// </summary>
/// <param name="destination">The destination of the data. It's required that the destination collection has a factory set.</param>
public DataProjectorToIEntityCollection2Custom(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.
field.ForcedCurrentValueWrite(value, value);
fieldSet = true;
}
}
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
{
newInstance.IsNew = false;
_destination.Add(newInstance);
}
}
}