Validation of FKs

Posts   
 
    
Posts: 134
Joined: 04-Mar-2005
# Posted on: 04-Oct-2005 22:18:09   

In the normal course of events FKs are usually "validated" due to the fact that they're selected from a dropdown (when the user has this level of control) however I have an instance where the number of items is too large for a dropdown and often the users know the FK, so it makes sense to allow the user to type FK and then validate it at save time. Allowing free-form entry of FKs raises the possibility that the user will type in an incorrect FK and violate a constraint. Unfortunately not all my FKs are constraint-enforced (Oracle doesn't support FK constraints to views) so I can't capture a constraint exception. So.. (I'm getting to my point - really flushed ) what I would like to do is add code to the validation (I use entity-level validation) to loop through each m:1 relation, build a predicate, and validate that the Foreign entity exists. I can see several components that would be useful (Fields(fieldIndex).IsForeignKey, GetRelationInfoxxx()) but I can't seem to link these together in any cohesive manner to do what I want. Any ideas?

pseudo code:

for each m:1 Relationship
  if Fields(index).IsChanged and Fields(index).IsForeignKey and (perhaps) Fields(Index).ValidateFK
    GetRelationInfo for this FK
    FetchEntity/GetDBCount
    if entityExists/DBCount > 0
      valid
    else
      not valid
    endif
  endif
next
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 05-Oct-2005 11:51:35   

I think that by using a template you can generate these out in code. To limit verbosity of the code, you can perhaps make the code abit generic. Would that help you in this?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 134
Joined: 04-Mar-2005
# Posted on: 05-Oct-2005 15:21:14   

Otis wrote:

I think that by using a template you can generate these out in code. To limit verbosity of the code, you can perhaps make the code abit generic. Would that help you in this?

That's exactly what I'd like to do. I'm fine with modifying/adding templates, what I'm unclear on is what the code that I need to write will look like... confused

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 05-Oct-2005 22:52:26   

ChicagoKiwi wrote:

Otis wrote:

I think that by using a template you can generate these out in code. To limit verbosity of the code, you can perhaps make the code abit generic. Would that help you in this?

That's exactly what I'd like to do. I'm fine with modifying/adding templates, what I'm unclear on is what the code that I need to write will look like... confused

I'll write some template code tomorrow (thursday) simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 134
Joined: 04-Mar-2005
# Posted on: 06-Oct-2005 14:23:30   

Otis wrote:

I'll write some template code tomorrow (thursday) simple_smile

Thanks! That's above and beyond the call... but much appreciated! sunglasses

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 06-Oct-2005 17:28:35   

<[Foreach RelatedEntity ManyToOne CrLf]>
    <[ Foreach RelationField CrLf]>
        <[If IsForeignKey]>
            if(this.Fields[(int)<[CurrentEntityName]>FieldIndex.<[RelationFieldName]>].IsChanged)
            {
                IRelationPredicateBucket relationInfo = this.GetRelationInfo<[MappedFieldNameRelation]>();
                // fetch entity here
            }
        <[EndIf]>
    <[NextForeach]>
<[NextForeach]>

Something like this? Not tested, but should work.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 134
Joined: 04-Mar-2005
# Posted on: 07-Oct-2005 04:25:15   

Thanks for the code. I added to it a litle (and translated to VB.NET) and got the following:

Dim entityToValidate As My<[CurrentEntityName]>Entity = CType(containingEntity, My<[CurrentEntityName]>Entity)
Dim adapter As New DatabaseSpecific.DataAccessAdapter
<[Foreach RelatedEntity ManyToOne ]>
<[ Foreach RelationField ]>
<[If IsForeignKey]>
            If entityToValidate.Fields(CType(<[CurrentEntityName]>FieldIndex.<[RelationFieldName]>, Integer)).IsChanged Then
            Dim relationInfo As IRelationPredicateBucket = entityToValidate.GetRelationInfo<[MappedFieldNameRelation]>()
                    ' fetch entity here
            Dim collectionToFetch as New HelperClasses.EntityCollection(New FactoryClasses.My<[RelatedEntityName]>EntityFactory)
            adapter.FetchEntityCollection(collectionToFetch, relationInfo)

            If Not collectionToFetch.Count = 1
                Throw New ORMEntityValidationException("The <[RelationFieldName]> is not valid.", containingEntity)
            End If
            End If
<[EndIf]>
<[NextForeach]>
<[NextForeach]>

Which generates:

            Dim adapter As New DatabaseSpecific.DataAccessAdapter

            If entityToValidate.Fields(CType(SubprojectFieldIndex.ProjectId, Integer)).IsChanged Then
                Dim relationInfo As IRelationPredicateBucket = entityToValidate.GetRelationInfoProject()
                ' fetch entity here
                Dim collectionToFetch As New HelperClasses.EntityCollection(New FactoryClasses.MyProjectEntityFactory)
                adapter.FetchEntityCollection(collectionToFetch, relationInfo)

                If Not entityToFetch.Count = 1 Then
                    Throw New ORMEntityValidationException("The ProjectId is not valid.", containingEntity)
                End If
            End If

I haven't tested this yet for speed or to see how it works for new entities with new child entities. In this case how would it work if I was adding a new subproject to a project that didn't yet exist in the DB? But that's for tomorrow...

Posts: 134
Joined: 04-Mar-2005
# Posted on: 13-Oct-2005 16:32:26   

ChicagoKiwi wrote:

But that's for tomorrow...

Isn't it odd how tomorrow never comes? frowning

I finally got back to this and, as I suspected, if the save of a new Project and Subproject are in the same commit/transaction then the validation fails as the DB query to validate the projectId FK returns nothing - the project is not yet in the DB. How can I check to see whether the project is in the current transaction? Could this be as simple of checking entityToValidate.IsNew And entityToValidate.Project.IsNew? Additionally could I rely on a check on Not entityToValidate.Project is Nothing to circumvent the DB check entirely?

Something like:

 If Not entityToValidate.Project Is Nothing Then
'Assume the FK relationship is Valid
Else
If entityToValidate.Fields(CType(SubprojectFieldIndex.ProjectId, Integer)).IsChanged Then
Dim relationInfo As IRelationPredicateBucket = entityToValidate.GetRelationInfoProject()
' fetch entity here
Dim collectionToFetch As New HelperClasses.EntityCollection(New FactoryClasses.MyProjectEntityFactory)
adapter.FetchEntityCollection(collectionToFetch, relationInfo)

If Not entityToFetch.Count = 1 Then
Throw New ORMEntityValidationException("The ProjectId is not valid.", containingEntity)
End If
End If
End If

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 14-Oct-2005 12:54:57   

ChicagoKiwi wrote:

ChicagoKiwi wrote:

But that's for tomorrow...

Isn't it odd how tomorrow never comes? frowning

I finally got back to this and, as I suspected, if the save of a new Project and Subproject are in the same commit/transaction then the validation fails as the DB query to validate the projectId FK returns nothing - the project is not yet in the DB. How can I check to see whether the project is in the current transaction?

You can check that by checking its Transaction property. If that's null, the entity isn't in a transaction, it should be the same as Subproject.Transaction.

Though, entities are added to the transaction when they're about to be saved. So you can also try to use the Entitystate (entity.Fields.State) property, to see if that's New, or not.

Frans Bouma | Lead developer LLBLGen Pro