Adapter.FetchExcludedFields errors when a target field is read only

Posts   
 
    
saggett
User
Posts: 50
Joined: 12-Nov-2007
# Posted on: 20-Mar-2008 18:51:32   

Using LLBLGen v2.5 (December 5th 2007) with runtime libraries version 2.5.7.1129. Building for .NET 3.0 using Adapter pattern.

We get the below exception when executing the following code:

public void SaveEntityCollection(IEntityCollection2 collection, IEntityCollection2 entitiesToDelete, bool recurse) { DataAccessAdapter adapter = CreateAdapter(); UnitOfWork2 unitOfWork = new UnitOfWork2(); if (collection != null && collection.Count > 0) { //done so that fields can be included in any audit record FetchExcludedFieldsForCollection(collection); unitOfWork.AddCollectionForSave(collection, false, recurse); } if (entitiesToDelete != null && entitiesToDelete.Count > 0) unitOfWork.AddCollectionForDelete(entitiesToDelete); unitOfWork.Commit(adapter, true); }

    private void FetchExcludedFieldsForCollection(IEntityCollection2 collection)
    {
        if (collection == null || collection.Count == 0)
            return;
        IEntityCollection2 fetchedCollection = new EntityCollection(collection.EntityFactoryToUse);
        foreach (IEntity2 ent in collection)
            if (!ent.IsNew)
                fetchedCollection.Add(ent);
        if (fetchedCollection.Count == 0)
            return;
        ExcludeIncludeFieldsList fieldsToFetch = new ExcludeIncludeFieldsList(true);
        foreach (IEntityField2 field in fetchedCollection[0].Fields)
            if (!field.IsNullable && field.CurrentValue == null)
                fieldsToFetch.Add(field);
        DataAccessAdapter adapter = CreateAdapter();
        adapter.FetchExcludedFields(fetchedCollection, fieldsToFetch);
    }

The exception occurs on adapter.FetchExcludedFields(fetchedCollection, fieldsToFetch); :

Event Type: Error Event Source: Univar Q Error Logging Event Category: None Event ID: 100 Date: 20/03/2008 Time: 17:29:26 User: N/A Computer: UKI-PRICING01 Description: Timestamp: 20/03/2008 17:29:26 Message: HandlingInstanceID: 3a26dfcc-31f8-4b18-b715-30a7d9157db4

An exception of type 'SD.LLBLGen.Pro.ORMSupportClasses.ORMFieldIsReadonlyException' occurred and was caught.

03/20/2008 17:29:26 Type : SD.LLBLGen.Pro.ORMSupportClasses.ORMFieldIsReadonlyException, SD.LLBLGen.Pro.ORMSupportClasses.NET20, Version=2.5.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27 Message : The field 'BaanPriceGroupId' is read-only and can't be changed. Source : SD.LLBLGen.Pro.ORMSupportClasses.NET20 Help link : RuntimeVersion : 2.5.0.0 RuntimeBuild : 11192007 Data : System.Collections.ListDictionaryInternal TargetSite : Boolean ValidateValue(SD.LLBLGen.Pro.ORMSupportClasses.IEntityField2, System.Object ByRef, Int32) Stack Trace : at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.ValidateValue(IEntityField2 fieldToValidate, Object& value, Int32 fieldIndex) at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.SetValue(Int32 fieldIndex, Object value, Boolean performDesyncForFKFields) at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.SetNewFieldValue(String fieldName, Object value) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchExcludedFields(IEntityCollection2 entities, ExcludeIncludeFieldsList excludedIncludedFields) at UnivarQ.DataGateway.WorkerBase.FetchExcludedFieldsForCollection(IEntityCollection2 collection) in C:\Projects\UnivarQ\Current\Product\Production\Facade\DataGateway\WorkerBase.cs:line 90 at UnivarQ.LogicManagers.AddOnCostChangeImpactDeterminer.SaveCorePriceOffsetOnEntitiesAffectedByAddOnCostChange(Int32[] addOnCostIds, EntityCollection`1 corePriceOffsets) in C:\Projects\UnivarQ\Current\Product\Production\Facade\LogicManagers\AddOnCostChangeImpactDeterminer.cs:line 133 at UnivarQ.QService.UnivarQService.SaveCorePriceOffsetOnEntitiesAffectedByAddOnCostChange(Int32[] addOnCostIds, IEntityCollection2 corePriceOffsets) in C:\Projects\UnivarQ\Current\Product\Production\Services\QService\UnivarQService.cs:line 997 at SyncInvokeSaveCorePriceOffsetOnEntitiesAffectedByAddOnCostChange(Object , Object[] , Object[] ) at System.ServiceModel.Dispatcher.InvokeDelegate.Invoke(Object target, Object[] inputs, Object[] outputs) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

Additional Info:

MachineName : UKI-PRICING01 TimeStamp : 20/03/2008 17:29:26 FullName : Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a AppDomainName : /LM/W3SVC/1/Root/UnivarQServices-3-128505074757389169 ThreadIdentity : WindowsIdentity : NT AUTHORITY\NETWORK SERVICE

Category: General Priority: 0 EventId: 100 Severity: Error Title:Exception Management Application Exception Machine: UKI-PRICING01 Application Domain: /LM/W3SVC/1/Root/UnivarQServices-3-128505074757389169 Process Id: 10632 Process Name: c:\windows\system32\inetsrv\w3wp.exe Win32 Thread Id: 9960 Thread Name: Extended Properties:

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

Seeing that BaanPriceGroupId is being fetched to the entity directly from the db, it seems unreasonable that an error should occur when setting it because it's a read only field.

I have a second question that's connected with the above code. You can probably see that in our FetchExcludedFieldsForCollection method we're attempting to get all the fields contained within the entities prior to the save. This is done so that when the save is audited, all the fields are present in the entity for writing to the auditpricegroup table (the method was introduced to fix a bug whereby we're get a 'Cannot insert NULL into BaanPriceGroupId' on the AuditPriceGroup Insert. (AuditPriceGroup has all the same columns as price group, plus a few more - user, date, etc. ).

But this method is flawed, so my question is - is there any way to distinguish between a field that was excluded from a fetch and a field that just happens to be null or zero? If there isn't, would you consider adding it in a future request?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 21-Mar-2008 12:13:49   

PLease upgrade to the latest build of the runtime libraries first to eliminate any bugfixes which might have been made to fix your error.

About the check whether a field has been excluded: if entity.Fields[index].DbValue is null, and field isn't nullable, the field was excluded. If the field is nullable, you can't tell if the field was excluded.

Frans Bouma | Lead developer LLBLGen Pro
saggett
User
Posts: 50
Joined: 12-Nov-2007
# Posted on: 25-Mar-2008 10:24:48   

About the check whether a field has been excluded: if entity.Fields[index].DbValue is null, and field isn't nullable, the field was excluded. If the field is nullable, you can't tell if the field was excluded.

That's what I thought. Related to this, is there any way to distinguish between a Customer.Orders collection with a count of zero because the customer doesn't have any orders and a Customer.Orders collection with a count of zero because it hasn't been fetched? I ask because I've quite often wanted to write code to distinguish between the two, mainly for the purposes of writing 'safer' code (throwing an exception if the Orders of the Customer have not been fetched as expected).

I'll update to the latest build and get back to you on the above.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 25-Mar-2008 11:18:11   

No that's not determinable. The reason is that it would require state information being carried around but that state information is actually local to the fetch action performed. So say this info is stored inside the collection, you then want to know which filter is used, because the filter limited the set to fetch.

It also gives a false reason to assume 'oh there aren't any orders'. That's not the case. At the moment of fetch (but when was that? an hour ago?) there weren't any orders matching the filter.

Also, your code knows what it did: if you ask for customers + orders, the code knows the orders have been fetched. So if your routine calls a BL routine which should give you customer + orders given the customerID passed in, it should give you the orders. The code calling that routine can assume the orders are there IF available. So no orders in the customers, no orders available. You then have to test your BL routine if that routine gives back the orders if it is called. That's it. After you've tested that, the routine obeys it's contract and you can build on top of that.

Frans Bouma | Lead developer LLBLGen Pro