Field Relations - how to get them?

Posts   
 
    
Koubessus
User
Posts: 28
Joined: 30-Dec-2004
# Posted on: 30-Dec-2004 12:54:40   

Hi.

I am currently working on a generic custom control which is supposed to create a web form based on a provided template. I was able to create a basic form based on generated EntityClass (IEntity2) but now I came to more advanced web controls and came to a stop while looking for some info about relation definied for the field.

Basically what I do is: 1. create an empty Entity 2. get fields array using entity.Fields.GetAsEntityFieldCoreArray() 3. if the field is foreign key I want to create a dropdownlist control and fill it with items based on this relation.

The question is: is there a way how to get some info about a relation (IEntityRelation for example) from EntityField, Entity or anything at all?

I've found the Relations property generated for each EntityClass but I can't find a way to use it without some obscure construction using reflection.

Any help would be great.

Thanks,

Jakub

Posts: 112
Joined: 09-Aug-2004
# Posted on: 30-Dec-2004 14:19:07   

My first suggestion would have been reflection.

I did something similar to this but each field has an XML node for it which contains that information. This way, if needed, users could actually control what and how each control shows on the form.

How about looking at the type of the field. If it is a collection, then it is a 1:n relation.

I also believe there is a property of a field, IsForeignKey.

Entity.Fields[X].IsForeignKey

Be sure to share the code once finished.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 30-Dec-2004 17:53:42   

The information isn't present in an entity instance, only in code for example through the relation objects.

The problem isn't that easy to solve, as compound foreign keys for example will result in 1 m:1 or 1:1 relation so what to do with those? Also, entities which are on the PK side of a relation (1:n or 1:1), don't have fk fields but do have fields mapped on relations.

'best' way to solve this is through code generation.

Frans Bouma | Lead developer LLBLGen Pro
Koubessus
User
Posts: 28
Joined: 30-Dec-2004
# Posted on: 30-Dec-2004 19:16:32   

Otis wrote:

The information isn't present in an entity instance, only in code for example through the relation objects.

The problem isn't that easy to solve, as compound foreign keys for example will result in 1 m:1 or 1:1 relation so what to do with those? Also, entities which are on the PK side of a relation (1:n or 1:1), don't have fk fields but do have fields mapped on relations.

'best' way to solve this is through code generation.

Actually, I am interested in m:1 (maybe 1:1) relations at the moment. The goal is to fill-in a dropdownlist or similar controls like checkboxlist.

I was looking and the Template Studio and was able to add my custom template into the entity class. But this would still require either Reflection or editing the entityAdapter template itself to implement my custom Interface. And I am trying to leave the original templates alone this time.

So I took the advice from lethologica and ended with this (it's based on analysis of the original templates):


private IEntityRelation[] GetEntityRelations(System.Type entityType)
{
  ArrayList arrRel = new ArrayList();
  PropertyInfo pi = entityType.GetProperty ("Relations", BindingFlags.Static | BindingFlags.Public);

  if (pi != null) 
  {
    //Get Relations Class
    object rels = pi.GetValue(null, new object[] {});

    if (rels != null) {
      foreach (PropertyInfo info in rels.GetType().GetProperties()) {
        if (info.PropertyType.Equals(typeof(IEntityRelation))
          || info.PropertyType.IsSubclassOf(typeof(IEntityRelation))) {
          arrRel.Add ((IEntityRelation) info.GetValue(rels, new object[]{}));
        }
      }
    }

  }
  return (IEntityRelation[]) arrRel.ToArray (typeof(IEntityRelation));
}

as this is used only for m:1 relations, I can choose appropriete relation based on IEntityFieldCore:


private IEntityRelation GetRelationByField (IEntityRelation[] relations, IEntityFieldCore field) {
  for (int i = 0; i < relations.Length; i++)
  {
    IEntityRelation rel = relations[i];
    if ((rel.TypeOfRelation == RelationType.ManyToOne) &&
      (rel.GetAllFKEntityFieldCoreObjects().Count == 1) &&
      (rel.GetFKEntityFieldCore(0).Name == field.Name))
      return rel;
  }
  return null;
}

Keep in mind that I am using System.Type of a EntityClass as a template for a form creation. I am assuming that I have all the entities in one namespace and assembly. Than I use relation for some "nasty" stuff I am not really proud of but it makes the job done.


System.Type classType = my original entity type

IEntityRelation relation = GetRelationByField(relations, srcField);
IEntityFieldCore primField= relation.GetPKEntityFieldCore(0);

string factoryName = classType.AssemblyQualifiedName.Replace ("EntityClasses." + classType.Name, "FactoryClasses." + primField.ContainingObjectName + "Factory");

string collName = classType.AssemblyQualifiedName.Replace ("EntityClasses." + classType.Name, "HelperClasses.EntityCollection");

EntityCollectionBase2 coll = GetCollection(collName, factoryName);


Collection is created using code from this: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=712


private EntityCollectionBase2 GetCollection (string collectionName, string factoryName) {
  EntityCollectionBase2 ents = null;
  System.Type factoryType = System.Type.GetType(factoryName, true, true);
  System.Type collectionType = System.Type.GetType(collectionName, true, true);

  IEntityFactory2 fac = (IEntityFactory2) factoryType.GetConstructor(new System.Type[]{}).Invoke(null);

  if (fac != null) 
  {
    ConstructorInfo ctor = collectionType.GetConstructor(new System.Type[]{typeof(IEntityFactory2)});
    ents = (EntityCollectionBase2) ctor.Invoke(new object[] {fac});
  }

  if (ents != null)
    Adapter.FetchEntityCollection(ents, new RelationPredicateBucket());
  return ents;

}

This way I can create the form and fill it with data while using already compiled LLBLGen generated projects. I am planning to use class and field Custom properties to set some specific information (like what field should be used for text value in dropdownlist, whether to create dropdownlist or checkboxlist, etc.) but the basic settings will be done using standard field properties (I can use first string field for a text value,...)

I think I will look more into the code generation and try to extend or add factory classes this time - it may be easier and less intrusive this way - but it will have to wait for a while.

Anyway, thanks for all the help.

Jakub

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 03-Jan-2005 10:57:41   

Thanks for posting this on the forum! simple_smile

The reason I went for code-generation was that it's hard to get a list of relations in an indexed store if you allow compound FK's too.

Frans Bouma | Lead developer LLBLGen Pro
mafti23
User
Posts: 13
Joined: 12-Jun-2006
# Posted on: 17-Jul-2006 15:42:02   

and is there an easier way atm?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 17-Jul-2006 16:02:27   

No, as the problem hasn't changed.

Frans Bouma | Lead developer LLBLGen Pro