define a custom relation/prefetch path

Posts   
 
    
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 15-Feb-2008 17:34:54   

Hi,

The situation:

I have an existing customer table that is defined like this:

Table: Lookups
  Id (int) [PK]
  TypeId (nvarchar) [PK]
  Order (int)
  Description (nvarchar)

that other tables reference in a weak relation like this:


Table: Others
   TypeIdlookupId (int)

Where **TypeId **is the value of the TypeId in the lookups table. A horrible way of defining relations, I know, but I can't change the database.

Still i would like to be able to have a property mapped on this relation and prefetchpath for the _LookupEntity _ on OtherEntity, so I've manually defined:

  • A Property (LookupEntity TypeIdLookup)
  • A Relation:
public virtual IEntityRelation TypeIdLookupUsingTypeIdlookupId
{
  get
  {
      IEntityRelation relation = new EntityRelation(RelationType.ManyToOne, "TypeIdLookup", false);
      relation.AddEntityFieldPair(LookupFields.Id, ContractFields.LegalHierarchyLookupId);
      relation.CustomFilter = new PredicateExpression(LookupFields.TypeId=="TypeId");
      relation.InheritanceInfoPkSideEntity = InheritanceInfoProviderSingleton.GetInstance().GetInheritanceInfo("LookupEntity", false);
      relation.InheritanceInfoFkSideEntity = InheritanceInfoProviderSingleton.GetInstance().GetInheritanceInfo("OtherEntity", true);
      return relation;
  }
}

A Prefetch path:

public static IPrefetchPathElement2 PrefetchTypeIdLookup
{
    get
    {
        return new PrefetchPathElement2(new EntityCollection(EntityFactoryCache2.GetEntityFactory(typeof(LookupEntityFactory))),
                                        Relations.TypeIdLookupUsingTypeIdlookupId,
                                        (int)EntityType.OtherEntity, (int)EntityType.LookupEntity, 0, null, new PredicateExpression(LookupFields.TypeId == "TypeId"), null, null, "TypeIdLookup", RelationType.ManyToOne);
    }
}

Now the relation and the prefetch path work fine, the entity is fetched, however the related property is never set, because in DataAccessAdapterBase.MergeNormal line 6016 the FKHash (based on **TypeIdlookupId, 1 field) is obviously different from the PKhash (Id **and TypeId, 2 fields).

So, to make long story short, what I would like to do is define a new (runtime) field for OtherEntity that always returns a fixed value, and add this to my relation field mapping:


  relation.AddEntityFieldPair(LookupFields.Id, ContractFields.LegalHierarchyLookupId);
  relation.AddEntityFieldPair(LookupFields.TypeId, new FixedValueField("TypeId")); 

Would you say this is the way to go, or is there an alternative solution?

Also, in the partial class of OtherEntity, I have to hook into this method:

 public override void SetRelatedEntityProperty(string propertyName, IEntity2 entity){}

to add functionality like this:

switch(propertyName)
  case "TypeIdLookup":
    this.TypeIdLookup = (LookupEntity)entity;
    break;

in order set my related entity. Which method of the _EntityBase2 _should I override, or event should I attach to in order to set this property?

Unfortunately I can't use and inherited business object, because I also use entity inheritance for OtherEntity.

Thank you in advance,

Wiebe Tijsma

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 16-Feb-2008 10:46:29   

This is a m:n relation as it seems, as you can have more than 1 lookup for every typeidlookup value in other. So the relation other - lookup isn't m:1 as it seems, but m:n. Does that clarify things a bit?

Frans Bouma | Lead developer LLBLGen Pro
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 16-Feb-2008 14:57:17   

Otis wrote:

This is a m:n relation as it seems, as you can have more than 1 lookup for every typeidlookup value in other. So the relation other - lookup isn't m:1 as it seems, but m:n. Does that clarify things a bit?

Hmm no there is only 1 related lookup entity for each TypeIdLookupId value. The problem is that the **TypeId **of the lookup is actually in the fieldname instead of having it's own LookupTypeId field with the corresponding value, because that would allow the relation to be explicitly created in the database.

Logically, it's defined as N:1 relation, but unfortunately physically it isn't.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 18-Feb-2008 10:57:18   

wtijsma wrote:

Otis wrote:

This is a m:n relation as it seems, as you can have more than 1 lookup for every typeidlookup value in other. So the relation other - lookup isn't m:1 as it seems, but m:n. Does that clarify things a bit?

Hmm no there is only 1 related lookup entity for each TypeIdLookupId value. The problem is that the **TypeId **of the lookup is actually in the fieldname instead of having it's own LookupTypeId field with the corresponding value, because that would allow the relation to be explicitly created in the database.

Logically, it's defined as N:1 relation, but unfortunately physically it isn't.

Hmm. then I'm afraid you're out of luck.

THough the problem isn't that big I think. In general, lookup entities often don'thave to be prefetched as they're mainly used in UI's. The advantage is that you can use databinding features to set lookup values without prefetching the lookup entities. So you could keep the relation you defined but drop the prefetch feature. See the 'Complex databinding example (VS.NET 2005)' example on our website how to do this.

Frans Bouma | Lead developer LLBLGen Pro
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 19-Feb-2008 12:01:19   

Otis wrote:

wtijsma wrote:

Otis wrote:

This is a m:n relation as it seems, as you can have more than 1 lookup for every typeidlookup value in other. So the relation other - lookup isn't m:1 as it seems, but m:n. Does that clarify things a bit?

Hmm no there is only 1 related lookup entity for each TypeIdLookupId value. The problem is that the **TypeId **of the lookup is actually in the fieldname instead of having it's own LookupTypeId field with the corresponding value, because that would allow the relation to be explicitly created in the database.

Logically, it's defined as N:1 relation, but unfortunately physically it isn't.

Hmm. then I'm afraid you're out of luck.

THough the problem isn't that big I think. In general, lookup entities often don'thave to be prefetched as they're mainly used in UI's. The advantage is that you can use databinding features to set lookup values without prefetching the lookup entities. So you could keep the relation you defined but drop the prefetch feature. See the 'Complex databinding example (VS.NET 2005)' example on our website how to do this.

I'm not doing databinding, I've created a WCF Service layer exposing DTO's that include the lookup descriptions.

Something I'm trying out now is using the LLBLGen UI to add a custom key/value pair to my TypeIdLookupId with the value of the TypeId, and overriding the 'MergeNormal' method in the DataAccessAdapter with my own custom implementation.

That should work I think, any hints on which method to override to hook into:

public override void SetRelatedEntityProperty(string propertyName, IEntity2 entity){}

Thanks! Wiebe

wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 19-Feb-2008 14:27:31   

wtijsma wrote:

That should work I think, any hints on which method to override to hook into:

public override void SetRelatedEntityProperty(string propertyName, IEntity2 entity){}

Thanks! Wiebe

Ok never mind that one, I have the property name so I'll just use reflection to set the property.

Another related question:

If I have a inheritance hierarchy, (LeaseContractEntity => ContractEntity), and I only have a reference to an IEntity2 which contains an instance of LeaseContractEntity, how do I get the FieldsCustomProperties of the base entity?

Requesting the FieldsCustomProperties doesn't return the FieldsCustomProperties included the base entity cry

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39873
Joined: 17-Aug-2003
# Posted on: 20-Feb-2008 12:38:44   

Hmm, yes that's indeed the case... This is done to prevent clashes with custom properties with the same name but not the same value: what to do with those? You've to use reflection to obtain the properties of the base types, I'm afraid.

Frans Bouma | Lead developer LLBLGen Pro
wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 20-Feb-2008 16:43:39   

Otis wrote:

Hmm, yes that's indeed the case... This is done to prevent clashes with custom properties with the same name but not the same value: what to do with those? You've to use reflection to obtain the properties of the base types, I'm afraid.

Hi Frans,

Thanks, I've solved it now by creating an interface ICustomPropertiesAccessor that I implement on all subtypes of other entities.

I've got it all working now, in the MergeNormal() override of my DataAccessAdapter I added a check for these custom properties.

Additionally to prevent having to use the FieldCustomProperties I created an attribute that I can add to my related property (StaticForeignKeyValueAttribute), and check for this attribute in MergeNormal.

Unfortunately it's not 100% efficient because I can't reuse the IEntityCollectionAccess2 as it's internal, so I can't access the CachedPkHashes if they're already created.

I had to recreate this functionality in my own ICachedEntityCollectionAccess interface, implemented on EntityCollection, maybe in the future you could expose this interface publicy?

Thanks!

Wiebe