omborddata wrote:
Hi Frans,
I'm using the DataProjectorToIEntityCollection2 and the version of LLBLGen is the latest, 4.2
Ok. It's a little unfortunate. The thing is, we can't change its behavior now as that would be a breaking change (as some code might rely on it). It's not that hard to work around it though. Below is the projector you're using, however I've adjusted it to set IsNew to false and to use the Forced write method so ischanged and isdirty isn't set.
////////////////////////////////////////////////////////////////////////////////////////////////////////
// LLBLGen Pro is (c) 2002-2011 Solutions Design. All rights reserved.
// http://www.llblgen.com
// The sourcecode for the ORM Support classes has been made available to LLBLGen Pro licensees
// so they can modify, update and/or extend it. Distribution of this sourcecode in textual, non-compiled,
// non-binary form to non-licensees is prohibited. Distribution of binary compiled versions of this
// sourcecode to non-licensees has been granted under the following license.
////////////////////////////////////////////////////////////////////////////////////////////////////////
// COPYRIGHTS:
// Copyright (c)2002-2011 Solutions Design. All rights reserved.
// http://www.llblgen.com
//
// The ORM Support classes library sourcecode is released to LLBLGen Pro licensees under the
// following license:
// --------------------------------------------------------------------------------------------
//
// Redistribution and use of the sourcecode in compiled, binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1) Redistributions must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
// 2) Redistribution of the sourcecode in textual, non-binary, non-compiled form is prohibited.
//
// THIS SOFTWARE IS PROVIDED BY SOLUTIONS DESIGN ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOLUTIONS DESIGN OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the authors
// and should not be interpreted as representing official policies, either expressed or implied,
// of Solutions Design.
//////////////////////////////////////////////////////////////////////
// Contributers to the code:
// - Frans Bouma [FB]
//////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;
using System.Data;
namespace SD.LLBLGen.Pro.ORMSupportClasses
{
/// <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 readonly IEntityCollectionCore _destination;
private readonly Dictionary<string, PropertyDescriptor> propertyDescriptors;
#endregion
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;
IEntityCore 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>
/// Initializes the projector, using the projectors if necessary.
/// </summary>
/// <param name="valueProjectors">The value projectors.</param>
/// <param name="dataSource">The data source. Open datareader to project from.</param>
public virtual void Initialize(List<IDataValueProjector> valueProjectors, IDataReader dataSource)
{
// nop
}
/// <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)
{
IEntityCore newInstance = _destination.EntityFactoryToUse.Create();
bool fieldSet = false;
for(int i = 0; i < projectors.Count; i++)
{
IProjector projector = (IProjector)projectors[i];
var fieldIndex = newInstance.Fields.GetFieldIndex(projector.ProjectedResultName);
object value = rawProjectionResult[i];
if((value != null) && value.Equals(DBNull.Value))
{
value = null;
}
if(fieldIndex < 0)
{
// set via property descriptor
PropertyDescriptor property;
if(propertyDescriptors.TryGetValue(projector.ProjectedResultName, out property))
{
property.SetValue(newInstance, value);
fieldSet = true;
}
}
else
{
newInstance.Fields.ForcedValueWrite(fieldIndex, 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((IEntityCore)rawProjectionResult[0]);
}
else
{
_destination.Add(newInstance);
}
newInstance.IsNew = false;
}
}
}
I hope it compiles (as I changed the code outside vs.net).
In general if a proc resultset is used, it's often more useful to map a typedview on the resultset. You can then fetch that typedview with queryspec, but in the case of writable / mutateable data that's of course not an option.
It's not a new problem, see: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=15392 (that code is similar to the one I posted above but won't work with v4 as the architecture slightly changed) And we know it's 6 years old, and the issue has been in our JIRA for quite some time but never got a high priority. I've marked it to be fixed for v5 as we can make breaking changes going into that version.
Hope this helps.