Validation Issues

Posts   
 
    
Posts: 98
Joined: 09-Feb-2005
# Posted on: 20-Jun-2006 00:04:51   

Adapter, Sql2k Adapter, SQL 2k

I have an asset supertype and inherited collateral subtype, built off of 2 separate tables (tbl_Asset, tbl_Collateral).

tbl_Asset has an Identity Int field (AssetId). Collateral is related on this field. (Not an identity field).

tbl_Lookup_Language LanguageId (int, identity) Name (varchar)

tbl_Asset_Language AssetId (int) LanguageId(int)


    public partial class CollateralValidator : AssetValidator
    {
        public const string NO_COLLATERALTYPE = "Please specify a collateral type";
        public const string NO_INITIATOR = "Please specify the initiator.";
        public const string NO_INDUSTRYSCENARIOS = "Please specify at least one industry scenario.";
        public const string NO_INDUSTRYSUBSEGMENTS = "Please specify at least one industry/subsegment.";
        public const string NO_SERVICES = "Please specify at least one service.";
        public const string NO_SOLUTIONS = "Please specify at least one solution.";

        public override void ValidateEntity(IEntityCore involvedEntity)
        {
            ErrorMessage.Length = 0;
            bool throwException = false;

            CollateralEntity collateral = (CollateralEntity)involvedEntity;
            Console.WriteLine("CollVal-ValEnt: Start" + collateral.Name + "-");
            try
            {
                base.ValidateEntity(involvedEntity);
            }
            catch (ORMEntityValidationException ex)
            {
                HandleError(ref throwException, ref collateral, ex.Message);
            }

            // First, run the generic asset validation
            // code that is applicable to all asset classes.    
            //AssetEntity asset = (AssetEntity) involvedEntity;
            //try 
            //{         
            //  asset.ValidateEntity();
            //}
            //catch (ORMEntityValidationException ex)
            //{ 
            //  // Is this ok??
            //  collateral = (CollateralEntity)involvedEntity;
            //  HandleError(ref throwException, ref collateral, ex.Message);
            //}

            if (collateral.CollateralType == null && collateral.CollateralTypeId == (int)TypeDefaultValue.GetDefaultValue(typeof(int)))
                HandleFieldError(ref throwException, ref collateral, CollateralFieldIndex.CollateralTypeId.ToString(), NO_COLLATERALTYPE);

            CollateralInitiatorEntity collInit = collateral.CollateralInitiator;
            if (collInit == null ||
                (collInit.Industry == null && collInit.Service == null && collInit.Solution == null))
                HandleError(ref throwException, ref collateral, NO_INITIATOR);
            
            if (collateral.CollateralIndustryScenarioCollection.Count == 0)
                HandleError(ref throwException, ref collateral, NO_INDUSTRYSCENARIOS);

            if (collateral.CollateralIndustrySubSegmentCollection.Count == 0)
                HandleError(ref throwException, ref collateral, NO_INDUSTRYSUBSEGMENTS);

            if (collateral.CollateralServiceCollection.Count == 0)
                HandleError(ref throwException, ref collateral, NO_SERVICES);

            if (collateral.CollateralSolutionCollection.Count == 0)
                HandleError(ref throwException, ref collateral, NO_SOLUTIONS);

            Console.WriteLine("CollVal-ValEnt: End" + collateral.Name + "-" + throwException.ToString());

            if (throwException)
                throw new ORMEntityValidationException(ErrorMessage.ToString(), involvedEntity);
        }

        private void HandleError(ref bool throwException, ref CollateralEntity collateral, string errorMessage)
        {
            collateral.SetEntityError(errorMessage);
            ErrorMessage.Append(errorMessage);
            throwException = true;
        }

        private void HandleFieldError(ref bool throwException, ref CollateralEntity collateral, string fieldName, string errorMessage)
        {
            collateral.SetEntityFieldError(fieldName, errorMessage, true);
            ErrorMessage.Append(errorMessage);
            throwException = true;
        }

        public override void ValidateEntityBeforeSave(IEntityCore involvedEntity)
        {
            ValidateEntity(involvedEntity);
            //base.ValidateEntityBeforeSave(involvedEntity);
            /// 
            /// Not needed because the AssetValidator will call the Collateral's ValidateEntity anyway (because this IS a 
            /// collateral entity).  The collateral's ValidateEntity method will call the AssetEntity's ValidateEntity 
            /// method anyway.
            /// 
            //ValidateEntity(involvedEntity);  
        }
    }



public partial class AssetValidator : ValidatorBase
    {
        public const string NO_LANGUAGE = "Please specify at least 1 language.";
        public const string NO_ASSETTYPE = "Please select an asset type.";
        public const string NO_CUSTOMER = "Please select a customer.";
        public const string NO_DOCUMENTLOCATION = "Please give us a link to the document.";
        public const string NO_NAME= "Please provide a name.";
        public const string NO_OWNER = "Please specify an owner.";
        internal StringBuilder _sbError;
        internal StringBuilder ErrorMessage
        {
            get
            {
                if (_sbError == null)
                    _sbError = new StringBuilder();
                else
                    _sbError.Append(Environment.NewLine);
                return _sbError;
            }
        }

        public override void ValidateEntity(IEntityCore involvedEntity)
        {
            ErrorMessage.Length = 0;
            bool throwException = false;
            AssetEntity asset = (AssetEntity)involvedEntity;
            Console.WriteLine("AssVal-ValEnt: Start" + asset.Name + "-");
            try
            {
                base.ValidateEntity(involvedEntity);
            }
            catch (ORMEntityValidationException ex)
            {
                HandleError(ref throwException, ref asset, ex.Message);
            }

            if (asset.AssetLanguageCollection.Count == 0)
                HandleError(ref throwException, ref asset, NO_LANGUAGE);

            if (asset.AssetType == null && asset.AssetTypeId == (int)TypeDefaultValue.GetDefaultValue(typeof(int)))
                HandleFieldError(ref throwException, ref asset, AssetFieldIndex.AssetTypeId.ToString(), NO_ASSETTYPE);

            if (asset.Customer == null && asset.CustomerId == (int)TypeDefaultValue.GetDefaultValue(typeof(int)))
                HandleFieldError(ref throwException, ref asset, AssetFieldIndex.CustomerId.ToString(), NO_CUSTOMER);

            if (string.IsNullOrEmpty(asset.DocumentLocation))
                HandleFieldError(ref throwException, ref asset, AssetFieldIndex.DocumentLocation.ToString(), NO_DOCUMENTLOCATION);

            if (string.IsNullOrEmpty(asset.Name))
                HandleFieldError(ref throwException, ref asset, AssetFieldIndex.Name.ToString(), NO_NAME);

            if (string.IsNullOrEmpty(asset.UserAdname))
                HandleFieldError(ref throwException, ref asset, AssetFieldIndex.UserAdname.ToString(), NO_OWNER);

            Console.WriteLine("AssVal-ValEnt: End" + asset.Name + "-" + throwException.ToString());

            if (throwException)
                throw new ORMEntityValidationException(ErrorMessage.ToString(), involvedEntity);
            
        }

        private void HandleError(ref bool throwException, ref AssetEntity asset, string errorMessage)
        {
            asset.SetEntityError(errorMessage);
            ErrorMessage.Append(errorMessage);
            throwException = true;
        }

        private void HandleFieldError(ref bool throwException, ref AssetEntity asset, string fieldName, string errorMessage)
        {
            asset.SetEntityFieldError(fieldName, errorMessage, true);
            ErrorMessage.Append(errorMessage);
            throwException = true;
        }

        public override void ValidateEntityBeforeSave(IEntityCore involvedEntity)
        {
            //base.ValidateEntityBeforeSave(involvedEntity);
            ValidateEntity(involvedEntity);
        }
    }

Ok, finally the problem... :-) When I try to save a collateral, ValidateEntityBeforeSave is triggered on the collateral entity, which executes CollateralValidator.ValidateEntity. This in turn calls AssetValidator.ValidateEntity. All is good up to this point. The code is executed the way I expect, and the validation passes. Unfortunately, AssetValidator.ValidateEntity is executed again somewhere, and THIS time, I fail my validation. (Specifically, the asset no longer has an AssetLanguage item.)

Unfortunately, I don't know when this code is executed, as it is in the bowels of the DataAccessAdapter.

I do have this enormous trace log though. Sorry I edited as much as I felt it was safe to edit. Search for: <SNIP>HERE IS WHERE I THINK THE PROBLEM IS. AssetId = 0</SNIP> to see where I think things go awry. Sorry for how long this wound up.

<SNIP>********************************************************</SNIP>

Method Enter: DataAccessAdapterBase.SaveEntity(4) Active Entity Description: Entity: sap.valuecollateral.dl.EntityClasses.CollateralEntity. ObjectID: 7900cc4f-3a97-4e2c-a04a-21462b84742e PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> Method Enter: DataAccessAdapterBase.DetermineActionQueues(7) Active Entity Description: Entity: sap.valuecollateral.dl.EntityClasses.CollateralEntity. ObjectID: 7900cc4f-3a97-4e2c-a04a-21462b84742e PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value>

<SNIP>********************************************************</SNIP>

Entity added to insert queue: Entity: sap.valuecollateral.dl.EntityClasses.LookupLanguageEntity. ObjectID: 7eead937-b819-4e21-a903-b510a6cfd898 PrimaryKey field: LanguageId. Type: System.Int32. Value: <undefined value>

<SNIP>********************************************************</SNIP>

Entity added to insert queue: Entity: sap.valuecollateral.dl.EntityClasses.CollateralEntity. ObjectID: 7900cc4f-3a97-4e2c-a04a-21462b84742e PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> Entity added to insert queue: Entity: sap.valuecollateral.dl.EntityClasses.AssetEntity. ObjectID: 10fafdfc-c253-4929-8974-5d468fc82911 PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value>

<SNIP>********************************************************</SNIP>

Entity added to insert queue:
Entity: sap.valuecollateral.dl.EntityClasses.AssetLanguageEntity. ObjectID: ef8a0ff8-c435-4ed9-a461-a3d921a0e4b8
PrimaryKey field: AssetId. Type: System.Int32. Value: 0
PrimaryKey field: LanguageId. Type: System.Int32. Value: <undefined value>
Method Exit: DataAccessAdapterBase.DetermineActionQueues(7)
Method Enter: DataAccessAdapterBase.StartTransaction
Transaction name: RecursiveSave. Isolation level: ReadCommitted. Method Enter: DataAccessAdapterBase.OpenConnection
New connection created.
Connection physically opened. Method Exit: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.StartTransaction Method Enter: DataAccessAdapterBase.PersistQueue Persistence action info: Action: Insert. Queue length: 27

<SNIP>********************************************************</SNIP> (Another entity uses the language entity, so while saving this entity, the language was saved...)

Current persisted entity info: Entity: sap.valuecollateral.dl.EntityClasses.LookupLanguageEntity. ObjectID: 7eead937-b819-4e21-a903-b510a6cfd898 PrimaryKey field: LanguageId. Type: System.Int32. Value: <undefined value> Method Enter: CreateInsertDQ Method Enter: CreateSingleTargetInsertDQ Generated Sql query: Query: INSERT INTO [VC_to_Galaxy].[dbo].[tbl_Lookup_Language] ([Name], [Description]) VALUES (@Name, @Description);SELECT @LanguageId=SCOPE_IDENTITY() Parameter: @LanguageId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Output. Value: <undefined value>. Parameter: @Name : String. Length: 200. Precision: 0. Scale: 0. Direction: Input. Value: NameText. Parameter: @Description : String. Length: 1000. Precision: 0. Scale: 0. Direction: Input. Value: DescriptionText.

Method Exit: CreateSingleTargetInsertDQ
Method Exit: CreateInsertDQ
Method Enter: DataAccessAdapterBase.ExecuteActionQuery
Method Enter: DataAccessAdapterBase.OpenConnection
Method Exit: DataAccessAdapterBase.OpenConnection
Method Enter: Query.ReflectOutputValuesInRelatedFields

Syncing field LanguageId with parameter @LanguageId. Method Exit: Query.ReflectOutputValuesInRelatedFields Method Exit: DataAccessAdapterBase.ExecuteActionQuery Method Enter: DataAccessAdapterBase.FetchEntity(1) Active Entity Description: Entity: sap.valuecollateral.dl.EntityClasses.LookupLanguageEntity. ObjectID: 7eead937-b819-4e21-a903-b510a6cfd898 PrimaryKey field: LanguageId. Type: System.Int32. Value: 373

Method Enter: DataAccessAdapterBase.FetchEntityUsingFilter(3) Method Enter: CreatePagingSelectDQ Method Enter: CreateSelectDQ Method Enter: CreateSelectDQ Generated Sql query: Query: SELECT [VC_to_Galaxy].[dbo].[tbl_Lookup_Language].[LanguageID] AS [LanguageId], [VC_to_Galaxy].[dbo].[tbl_Lookup_Language].[VCID] AS [Vcid], [VC_to_Galaxy].[dbo].[tbl_Lookup_Language].[Name], [VC_to_Galaxy].[dbo].[tbl_Lookup_Language].[Description] FROM [VC_to_Galaxy].[dbo].[tbl_Lookup_Language] WHERE ( ( [VC_to_Galaxy].[dbo].[tbl_Lookup_Language].[LanguageID] = @LanguageId1)) Parameter: @LanguageId1 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 373.

Method Exit: CreateSelectDQ Method Exit: CreatePagingSelectDQ: no paging.

<SNIP>********************************************************</SNIP>

Data Supplying Entity Description:
Entity: sap.valuecollateral.dl.EntityClasses.LookupLanguageEntity. ObjectID: 624f9d3a-d3ba-4683-81f3-763ad08cb936
PrimaryKey field: LanguageId. Type: System.Int32. Value: 373

Syncing FK field LanguageId with PK field LanguageId Method Exit: EntityBase2.SyncFKFields Method Enter: EntityBase2.SyncFKFields Active Entity Description: Entity: sap.valuecollateral.dl.EntityClasses.AssetLanguageEntity. ObjectID: ef8a0ff8-c435-4ed9-a461-a3d921a0e4b8 PrimaryKey field: AssetId. Type: System.Int32. Value: 0 PrimaryKey field: LanguageId. Type: System.Int32. Value: <undefined value> Data Supplying Entity Description: Entity: sap.valuecollateral.dl.EntityClasses.LookupLanguageEntity. ObjectID: ef8a0ff8-c435-4ed9-a461-a3d921a0e4b8 PrimaryKey field: LanguageId. Type: System.Int32. Value: 373

Syncing FK field LanguageId with PK field LanguageId Method Exit: EntityBase2.SyncFKFields

<SNIP>********************************************************</SNIP>

Method Enter: EntityBase2.SyncFKFields
Active Entity Description:
Entity: sap.valuecollateral.dl.EntityClasses.AssetEntity. ObjectID: 10fafdfc-c253-4929-8974-5d468fc82911
PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value>
Data Supplying Entity Description:
Entity: sap.valuecollateral.dl.EntityClasses.LookupStatusEntity. ObjectID: 10fafdfc-c253-4929-8974-5d468fc82911
PrimaryKey field: StatusId. Type: System.Int32. Value: 248

Syncing FK field StatusId with PK field StatusId Method Exit: EntityBase2.SyncFKFields Current persisted entity info: Entity: sap.valuecollateral.dl.EntityClasses.CollateralEntity. ObjectID: 7900cc4f-3a97-4e2c-a04a-21462b84742e PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value> Method Enter: CreateInsertDQ Method Enter: CreateSingleTargetInsertDQ Generated Sql query: Query: INSERT INTO [VC_to_Galaxy].[dbo].[tbl_Asset] ([AssetTypeID], [CustomerID], [StatusID], [PublishDate], [ExpirationDate], [ExternalSystemID], [UserADName], [Name], [DocumentLocation], [Description], [CommentsInternal]) VALUES (@AssetTypeId, @CustomerId, @StatusId, @PublishDate, @ExpirationDate, @ExternalSystemId, @UserAdname, @Name, @DocumentLocation, @Description, @CommentsInternal);SELECT @AssetId_AssetEntity=SCOPE_IDENTITY() Parameter: @AssetId_AssetEntity : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Output. Value: <undefined value>. Parameter: @AssetTypeId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 2058. Parameter: @CustomerId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 326. Parameter: @StatusId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 248. Parameter: @PublishDate : DateTime. Length: 0. Precision: 23. Scale: 3. Direction: Input. Value: 6/14/2006 12:00:00 AM. Parameter: @ExpirationDate : DateTime. Length: 0. Precision: 23. Scale: 3. Direction: Input. Value: 6/14/2006 12:00:00 AM. Parameter: @ExternalSystemId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 57. Parameter: @UserAdname : String. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: SAP_ALL\C5067163. Parameter: @Name : String. Length: 250. Precision: 0. Scale: 0. Direction: Input. Value: NameText. Parameter: @DocumentLocation : String. Length: 600. Precision: 0. Scale: 0. Direction: Input. Value: DocumentLocationText. Parameter: @Description : String. Length: 4000. Precision: 0. Scale: 0. Direction: Input. Value: DescriptionText. Parameter: @CommentsInternal : String. Length: 1073741823. Precision: 0. Scale: 0. Direction: Input. Value: CommentsInternalText.

Method Exit: CreateSingleTargetInsertDQ Method Enter: CreateSingleTargetInsertDQ Generated Sql query: Query: INSERT INTO [VC_to_Galaxy].[dbo].[tbl_Collateral] ([AssetID], [CollateralTypeID], [NotifyDate], [MediaRequestNumber], [MaterialNumber], [LegacyLocation], [HasERPContract], [IsValueReference], [LetExpire], [LastEmailedDate], [ApplicationText], [BenefitsText]) VALUES (@AssetId, @CollateralTypeId, @NotifyDate, @MediaRequestNumber, @MaterialNumber, @LegacyLocation, @HasErpcontract, @IsValueReference, @LetExpire, @LastEmailedDate, @ApplicationText, @BenefitsText) Parameter: @AssetId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: <undefined value>. Parameter: @CollateralTypeId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 196. Parameter: @NotifyDate : DateTime. Length: 0. Precision: 23. Scale: 3. Direction: Input. Value: 6/14/2006 12:00:00 AM. Parameter: @MediaRequestNumber : AnsiString. Length: 20. Precision: 0. Scale: 0. Direction: Input. Value: MediaRequestNumberTe. Parameter: @MaterialNumber : AnsiString. Length: 20. Precision: 0. Scale: 0. Direction: Input. Value: MaterialNumberText. Parameter: @LegacyLocation : String. Length: 10. Precision: 0. Scale: 0. Direction: Input. Value: LegacyLoca. Parameter: @HasErpcontract : Boolean. Length: 0. Precision: 1. Scale: 0. Direction: Input. Value: True. Parameter: @IsValueReference : Boolean. Length: 0. Precision: 1. Scale: 0. Direction: Input. Value: True. Parameter: @LetExpire : Boolean. Length: 0. Precision: 1. Scale: 0. Direction: Input. Value: True. Parameter: @LastEmailedDate : DateTime. Length: 0. Precision: 23. Scale: 3. Direction: Input. Value: 6/14/2006 12:00:00 AM. Parameter: @ApplicationText : String. Length: 1000. Precision: 0. Scale: 0. Direction: Input. Value: ApplicationTextText. Parameter: @BenefitsText : String. Length: 1500. Precision: 0. Scale: 0. Direction: Input. Value: BenefitsTextText.

Method Exit: CreateSingleTargetInsertDQ
Method Exit: CreateInsertDQ
Method Enter: DataAccessAdapterBase.ExecuteActionQuery
Method Enter: DataAccessAdapterBase.OpenConnection
Method Exit: DataAccessAdapterBase.OpenConnection
Method Enter: Query.ReflectOutputValuesInRelatedFields

Syncing field AssetId with parameter @AssetId_AssetEntity. Method Exit: Query.ReflectOutputValuesInRelatedFields Method Enter: Query.ReflectOutputValuesInRelatedFields Method Exit: Query.ReflectOutputValuesInRelatedFields: no parameter relations. Method Exit: DataAccessAdapterBase.ExecuteActionQuery Method Enter: DataAccessAdapterBase.FetchEntity(1) Active Entity Description: Entity: sap.valuecollateral.dl.EntityClasses.CollateralEntity. ObjectID: 7900cc4f-3a97-4e2c-a04a-21462b84742e PrimaryKey field: AssetId. Type: System.Int32. Value: 23 PrimaryKey field: AssetId. Type: System.Int32. Value: 23

Method Enter: DataAccessAdapterBase.FetchEntityUsingFilter(3) Method Enter: CreatePagingSelectDQ Method Enter: CreateSelectDQ Method Enter: CreateSelectDQ Generated Sql query: Query: SELECT [VC_to_Galaxy].[dbo].[tbl_Asset].[AssetID] AS [AssetId_AssetEntity], [VC_to_Galaxy].[dbo].[tbl_Asset].[AssetTypeID] AS [AssetTypeId], [VC_to_Galaxy].[dbo].[tbl_Asset].[VCID] AS [Vcid], [VC_to_Galaxy].[dbo].[tbl_Asset].[CustomerID] AS [CustomerId], [VC_to_Galaxy].[dbo].[tbl_Asset].[StatusID] AS [StatusId], [VC_to_Galaxy].[dbo].[tbl_Asset].[PublishDate], [VC_to_Galaxy].[dbo].[tbl_Asset].[ExpirationDate], [VC_to_Galaxy].[dbo].[tbl_Asset].[ExternalSystemID] AS [ExternalSystemId], [VC_to_Galaxy].[dbo].[tbl_Asset].[UserADName] AS [UserAdname], [VC_to_Galaxy].[dbo].[tbl_Asset].[Name], [VC_to_Galaxy].[dbo].[tbl_Asset].[DocumentLocation], [VC_to_Galaxy].[dbo].[tbl_Asset].[Description], [VC_to_Galaxy].[dbo].[tbl_Asset].[CommentsInternal], [VC_to_Galaxy].[dbo].[tbl_Collateral].[AssetID] AS [AssetId], [VC_to_Galaxy].[dbo].[tbl_Collateral].[CollateralTypeID] AS [CollateralTypeId], [VC_to_Galaxy].[dbo].[tbl_Collateral].[NotifyDate], [VC_to_Galaxy].[dbo].[tbl_Collateral].[MediaRequestNumber], [VC_to_Galaxy].[dbo].[tbl_Collateral].[MaterialNumber], [VC_to_Galaxy].[dbo].[tbl_Collateral].[LegacyLocation], [VC_to_Galaxy].[dbo].[tbl_Collateral].[HasERPContract] AS [HasErpcontract], [VC_to_Galaxy].[dbo].[tbl_Collateral].[IsValueReference], [VC_to_Galaxy].[dbo].[tbl_Collateral].[LetExpire], [VC_to_Galaxy].[dbo].[tbl_Collateral].[LastEmailedDate], [VC_to_Galaxy].[dbo].[tbl_Collateral].[ApplicationText], [VC_to_Galaxy].[dbo].[tbl_Collateral].[BenefitsText] FROM ( [VC_to_Galaxy].[dbo].[tbl_Asset] INNER JOIN [VC_to_Galaxy].[dbo].[tbl_Collateral] ON [VC_to_Galaxy].[dbo].[tbl_Asset].[AssetID]=[VC_to_Galaxy].[dbo].[tbl_Collateral].[AssetID]) WHERE ( ( [VC_to_Galaxy].[dbo].[tbl_Asset].[AssetID] = @AssetId_AssetEntity1)) Parameter: @AssetId_AssetEntity1 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 23.

Method Exit: CreateSelectDQ Method Exit: CreatePagingSelectDQ: no paging. Method Enter: DataAccessAdapterBase.ExecuteSingleRowRetrievalQuery Method Enter: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.OpenConnection Method Exit: DataAccessAdapterBase.ExecuteSingleRowRetrievalQuery Method Exit: DataAccessAdapterBase.FetchEntityUsingFilter Method Exit: DataAccessAdapterBase.FetchEntity(1) Method Enter: EntityBase2.SyncFKFields

<SNIP></SNIP> <SNIP>HERE IS WHERE I THINK THE PROBLEM IS. AssetId = 0</SNIP> <SNIP></SNIP>

Active Entity Description:
Entity: sap.valuecollateral.dl.EntityClasses.AssetLanguageEntity. ObjectID: ef8a0ff8-c435-4ed9-a461-a3d921a0e4b8
PrimaryKey field: AssetId. Type: System.Int32. Value: 0
PrimaryKey field: LanguageId. Type: System.Int32. Value: 373
Data Supplying Entity Description:
Entity: sap.valuecollateral.dl.EntityClasses.CollateralEntity. ObjectID: ef8a0ff8-c435-4ed9-a461-a3d921a0e4b8
PrimaryKey field: AssetId. Type: System.Int32. Value: 23
PrimaryKey field: AssetId. Type: System.Int32. Value: 23

Syncing FK field AssetId with PK field AssetId Method Exit: EntityBase2.SyncFKFields Method Enter: EntityBase2.SyncFKFields Current persisted entity info: Entity: sap.valuecollateral.dl.EntityClasses.AssetEntity. ObjectID: 10fafdfc-c253-4929-8974-5d468fc82911 PrimaryKey field: AssetId. Type: System.Int32. Value: <undefined value>

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 20-Jun-2006 07:33:11   

Could you set a breakpoint in your validator class, and when it gets called the second time (thus when it fails), could you then in vs.net check the call stack?. Then you could see from where the call originates... (it should originate from DataAccessAdapterBase.PersistQueue, then entitybase2.CallValidateEntityBeforeSave etc. )

I'll try to dig up more info with your tracelog/code as well wink

(edit) the 'here's where I think the problem is' location is inside syncFK fields, which means it will there sync the just saved PK with the FK, which is typical in a pk-fk relation where 2 entities have to be saved and the PK side is saved first.

The only routine which calls CallValidateEntityBeforeSave (the entity validator internally isn't accessed directly by external code) is PersistQueue, which only works with entities (if an entity is in a hierarchy it might result in 2 or more insert queuries, but it's 1 entity for PersistQueue).

Frans Bouma | Lead developer LLBLGen Pro
Posts: 98
Joined: 09-Feb-2005
# Posted on: 20-Jun-2006 16:31:05   

Hey Frans, I added the source code projects and reran. Here's the stack trace:

sap.valuecollateral.dl.DLL!sap.valuecollateral.dl.ValidatorClasses. AssetValidator.ValidateEntity( SD.LLBLGen.Pro.ORMSupportClasses.IEntityCore involvedEntity = {sap.valuecollateral.dl.EntityClasses.AssetEntity}) Line 49    C#
sap.valuecollateral.dl.DLL!sap.valuecollateral.dl.ValidatorClasses. AssetValidator.ValidateEntityBeforeSave( SD.LLBLGen.Pro.ORMSupportClasses.IEntityCore involvedEntity = {sap.valuecollateral.dl.EntityClasses.AssetEntity}) Line 90 + 0x9 bytes  C#
SD.LLBLGen.Pro.ORMSupportClasses.DLL!SD.LLBLGen.Pro.ORMSupportClasses. EntityBase2.OnValidateEntityBeforeSave() Line 1702 + 0xb bytes   C#
SD.LLBLGen.Pro.ORMSupportClasses.DLL!SD.LLBLGen.Pro.ORMSupportClasses. EntityBase2.CallValidateEntityBeforeSave() Line 2117 + 0xa bytes C#
SD.LLBLGen.Pro.ORMSupportClasses.DLL!SD.LLBLGen.Pro.ORMSupportClasses. DataAccessAdapterBase.PersistQueue( System.Collections.Generic.List<SD.LLBLGen.Pro.ORMSupportClasses. ActionQueueElement<SD.LLBLGen.Pro.ORMSupportClasses.IEntity2>> queueToPersist = Count = 27, bool insertActions = true) Line 1427 + 0xa bytes   C#
SD.LLBLGen.Pro.ORMSupportClasses.DLL!SD.LLBLGen.Pro.ORMSupportClasses. DataAccessAdapterBase.SaveEntity( SD.LLBLGen.Pro.ORMSupportClasses.IEntity2 entityToSave = {sap.valuecollateral.dl.EntityClasses.CollateralEntity}, bool refetchAfterSave = true, SD.LLBLGen.Pro.ORMSupportClasses.IPredicateExpression updateRestriction = null, bool recurse = true) Line 1332 + 0x10 bytes    C#

SD.LLBLGen.Pro.ORMSupportClasses.DLL!SD.LLBLGen.Pro.ORMSupportClasses. DataAccessAdapterBase.SaveEntity( SD.LLBLGen.Pro.ORMSupportClasses.IEntity2 entityToSave = {sap.valuecollateral.dl.EntityClasses.CollateralEntity}, bool refetchAfterSave = true, bool recurse = true) Line 1222 + 0x2f bytes C# sap.valuecollateral.bl.DLL!sap.valuecollateral.bl.CollateralManager.Save( sap.valuecollateral.dl.EntityClasses.CollateralEntity collateral = {sap.valuecollateral.dl.EntityClasses.CollateralEntity}, bool refetchAfterSave = true, bool recurse = true) Line 137 + 0x14 bytes C# sap.valuecollateral.tests.DLL!sap.valuecollateral.tests.CollateralManagerTest.CreateCollateral() Line 174 + 0x12 bytes C# [External Code]

Thanks man. Appreciate your help as always.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 20-Jun-2006 16:53:20   

As you can see, the call originates directly from: sap.valuecollateral.tests.DLL!sap.valuecollateral.tests.CollateralManagerTest.CreateCollateral() Line 174

How can a save be called from a create routine, or is that ok? You save 1 entity there, or 2?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 98
Joined: 09-Feb-2005
# Posted on: 20-Jun-2006 17:24:30   

Hey Frans, the calling method is an nunit test. It creates a collateral, and then attempts to invoke the CollateralManager to save it. So what happens is that I step into the CollateralManager, which does the Adapter.Save thing. That immediately calls my validation logic, which passes. The stack that I pass here was captured in the validation logic on the execution run that fails. As you suspected, it happens inside of PersistQueue.

There is a lot of stuff being saved here at once. (It's an nunit test, so I have to build up a lot of data in memory, and then save it all at once.)

        
                [Test]
                public void CreateCollateral()
        {
            EnsureCompleteCollateralCollection();
            CollateralEntity coll = _help.CollateralCollection[0];
            _collateralManager.Save(coll, true, true);
            
            // Make sure the asset exists (shouldn't NOT hit the database and still have an ID.
            Assert.AreNotEqual(0, coll.AssetId);

            // Fetch from system, which assumes that the fetch works.
            CollateralCriteria cc = new CollateralCriteria();
            cc.AssetIDs = new int[]{coll.AssetId};
            CollateralEntity fetchedColl = _collateralManager.Fetch(cc);

            EntityComparer.Assert_CompareEntities(coll, fetchedColl);
        }

Anything else I can provide that might help? Thanks again.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 21-Jun-2006 12:05:51   

Please make absolutely sure the same instance of the validator isn't shared among different entities or that an entity which shouldn't get a validator doesn't get a validator by accident. The call is legit, it's simply called because an entity is saved so if that entity has a validator, the runtime will call into the validator. Check the objectid's inside the validator for the entity passed in, it's a different objectid I'm sure.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 98
Joined: 09-Feb-2005
# Posted on: 21-Jun-2006 18:00:56   

Will do, and update w/ results, but have a pressing issue to take care of first. Thanks Frans.

Posts: 98
Joined: 09-Feb-2005
# Posted on: 26-Jun-2006 20:04:03   

Update: I was wondering if something in my llbl project had become screwed up with all of my database changes, so before saving, I checked to see what llbl thought it would need to persist first. Since it dies trying to save a customer entity, I grabbed the customer before the save routine and called the GetDependingRelatedEntities() method. I got the Industry (saved first) and the UserProfileEntity, which should have been saved, but wasn't. So it seems that llbl does know what to save, but simply isn't doing it.


Ok, so I was working on a recursive class to populate test data for my tests. I am not getting the validation error right now, but I am getting an error while trying to save the generated data.

Here is the trace info for the statements being executed:

Method Exit: CreateSingleTargetInsertDQ Method Exit: CreateInsertDQ Method Enter: CreatePagingSelectDQ Method Enter: CreateSelectDQ Method Enter: CreateSelectDQ Generated Sql query: Query: SELECT [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[IndustryID] AS [IndustryId], [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[ParentIndustryID] AS [ParentIndustryId], [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[VCID] AS [Vcid], [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[IndustryTypeID] AS [IndustryTypeId], [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[Name], [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[Description] FROM [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry] WHERE ( ( [VC_to_Galaxy].[dbo].[tbl_Lookup_Industry].[IndustryID] = @IndustryId1)) Parameter: @IndustryId1 : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 58.

Method Exit: CreateSelectDQ Method Exit: CreatePagingSelectDQ: no paging. Method Enter: CreateInsertDQ Method Enter: CreateSingleTargetInsertDQ Generated Sql query: Query: INSERT INTO [VC_to_Galaxy].[dbo].[tbl_Customer] ([VCID], [MasterCode], [DUNS], [Name], [WebsiteURL], [CommentsInternal], [Accepted], [AcceptedDate], [IndustryID], [UserADName], [Revenue], [Employees]) VALUES (@Vcid, @MasterCode, @Duns, @Name, @WebsiteUrl, @CommentsInternal, @Accepted, @AcceptedDate, @IndustryId, @UserAdname, @Revenue, @Employees);SELECT @CustomerId=SCOPE_IDENTITY() Parameter: @CustomerId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Output. Value: <undefined value>. Parameter: @Vcid : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 100. Parameter: @MasterCode : AnsiString. Length: 10. Precision: 0. Scale: 0. Direction: Input. Value: MasterC 0. Parameter: @Duns : AnsiString. Length: 20. Precision: 0. Scale: 0. Direction: Input. Value: Duns 0. Parameter: @Name : String. Length: 200. Precision: 0. Scale: 0. Direction: Input. Value: Name 0. Parameter: @WebsiteUrl : String. Length: 400. Precision: 0. Scale: 0. Direction: Input. Value: WebsiteUrl 0. Parameter: @CommentsInternal : String. Length: 2000. Precision: 0. Scale: 0. Direction: Input. Value: CommentsInternal 0. Parameter: @Accepted : Boolean. Length: 0. Precision: 1. Scale: 0. Direction: Input. Value: False. Parameter: @AcceptedDate : DateTime. Length: 0. Precision: 23. Scale: 3. Direction: Input. Value: 6/26/2006 12:00:00 AM. Parameter: @IndustryId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 58. Parameter: @UserAdname : String. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: UserAdname 0. Parameter: @Revenue : Decimal. Length: 0. Precision: 18. Scale: 0. Direction: Input. Value: 100. Parameter: @Employees : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 100.

Method Exit: CreateSingleTargetInsertDQ Method Exit: CreateInsertDQ A first chance exception of type 'SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException' occurred in SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll

The problem is that the Customer that is being saved depends on the existence of a UserProfile. (They're joined by the UserAdName) The UserProfile exists (I can access Customer.UserAdName, which is 'UserAdName 0' as you can see from the trace. I can access Customer.UserProfile as well, which returns the appropriate UserProfile object.)

Any ideas why the recursive save function might be dying here?

I've created a test project (vs2k5, sql 2k) that will demonstrate the issue. Below is a link to a zip file that contains the database, llbl project, test code, and generated code. The generated code is slightly modified from the norm in that I have some validation classes added. I don't think they're the cause of the problem though right now, so they can probably be ignored. To that end, I've included a lib directory for the test project that just uses the dll's I've already generated.

http://www.thedevilles.net/llbl/llbltest.zip

Any help would be greatly appreciated. Thanks

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 27-Jun-2006 08:13:25   

What's the exception messsage, of the ORMQueryExecutionException?

Is this by any mean related to the previous post: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=6620

Posts: 98
Joined: 09-Feb-2005
# Posted on: 27-Jun-2006 14:30:06   

Sorry about that. Here is the exception below:

sap.valuecollateral.tests.TestHelper2.CreateAsset : SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException : An exception was caught during the execution of an action query: INSERT statement conflicted with COLUMN FOREIGN KEY constraint 'FK_tbl_Customer_tbl_UserProfile'. The conflict occurred in database 'VC_to_Galaxy', table 'tbl_UserProfile', column 'UserADName'. The statement has been terminated.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.

This is probably unrelated to the previous error. I put it here for no other reason than that I was trying to reproduce the previous problem when I ran into this one.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 27-Jun-2006 14:59:10   

The problem is that the Customer that is being saved depends on the existence of a UserProfile. (They're joined by the UserAdName) The UserProfile exists (I can access Customer.UserAdName, which is 'UserAdName 0' as you can see from the trace. I can access Customer.UserProfile as well, which returns the appropriate UserProfile object.)

When you say the UserProfile exists, are you sure it exists in the database (conflicts with the exception). It might be existing in memory but not saved to the database.

Please make sure the value of the Customer.UserAdName already exists in the database table "tbl_UserProfile".

Also please post the code snippet that generated this exception.

Thanks.

Posts: 98
Joined: 09-Feb-2005
# Posted on: 27-Jun-2006 15:53:20   

Hello Walaa, The database at this point is 100% empty. I'm using reflection to interrogate the entities and generate test data for my system. The problem occurs when I try to save. From the trace I sent above, it appears that the customer does have a UserProfile that is related to it, and both the UserAdName and UserProfile are accessible, which is as it should be. This UserProfile is marked IsNew as well.

If I update the llbl project so that the UserProfile does not maintain a collection of its customers, then the same error just shifts to a different table that references the UserProfile object.

I do have a thought as to what may be causing the problem. I specify the primary key for a UserProfile (the UserAdName). The entities that reference a userprofile actually are given the primary key for the user. Might this be why they don't think the UserProfile needs to be set? The database enforces referential integrity, so a UserAdName can not be used until it exists in the UserProfile table. Does LLBL not look at these restrictions?

If not, is there a suggested manner in which to handle this manually? It sounds as if I would have to add a wrapper to the adapter class that is aware of the referential integrity rules to persist things in the proper order.

Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 28-Jun-2006 10:18:45   

I'll give your testcode a spin this morning.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 28-Jun-2006 12:58:09   

recompiling and running your code indeed produces the exception, however why is a bit unclear to me as the Helper class needs some time to understand all of it. I guess it's the EnsureCollectionReferences routine which actually crashes, when inserting new testdata.

What I do see is that you use field.CurrentValue. That's a no-no. You can use that property, but if you want to persist that value, you have to use entity.SetNewFieldValue instead.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 98
Joined: 09-Feb-2005
# Posted on: 28-Jun-2006 18:31:17   

Hey Frans, My apologies, I know helper.cs is a bit of a mess. I've been tinkering. I've changed the CurrentValue to SetNewFieldValue, but since I only called this on fields that had no relations, it didn't have any impact. Is SetRelatedEntityProperty the right one to use for setting entities linked via relations?

Thank you for taking a look.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 28-Jun-2006 19:00:34   

jeffdeville wrote:

Hey Frans, My apologies, I know helper.cs is a bit of a mess. I've been tinkering. I've changed the CurrentValue to SetNewFieldValue, but since I only called this on fields that had no relations, it didn't have any impact. Is SetRelatedEntityProperty the right one to use for setting entities linked via relations?

No, you should use SetRelatedEntity, but better yet, use the property which represents the field mapped onto the relation, e.g. myOrder.Customer = myCustomer. If you don't know the property at compile time, use SetRelatedEntity, e.g.: myCustomer.SetRelatedEntity(myOrder, "Orders");

Do that on the OPPOSITE entity, e.g. the entity you want to assign. So if you want to do: myOrder.Customer = myCustomer; do: myCustomer.SetRelatedEntity(myOrder, "Orders");

See the generated properties for details on this simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 98
Joined: 09-Feb-2005
# Posted on: 28-Jun-2006 21:20:12   

Hey Frans, thank you for the input. I'll try and rewrite the helper class, but how do I know when I should use the opposite entity? Should I always try to assign from the PK element to the FK element?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 29-Jun-2006 17:25:58   

jeffdeville wrote:

Hey Frans, thank you for the input. I'll try and rewrite the helper class, but how do I know when I should use the opposite entity? Should I always try to assign from the PK element to the FK element?

You should use the opposite entity if you want to assign A to B and A is the FK side. So you should always use the PK side's method.

Frans Bouma | Lead developer LLBLGen Pro