Sam Beauvois: general dev, .net and other stuff

I want my foreign key to be mapped to a single object and not a collection

Say you have two tables:  Teams and Registrations

Registration has a foreign key for Teams :

image

In Subsonic 3, the Registration class will have a property called “Beach_Teams” which returns a IQueryable<Beach_Team> collection.

If we look at the generate class in the activerecord.cs file, there is a “Foreign Keys” region which contains the Beach_Teams property:


 #region ' Foreign Keys '
 public IQueryable<Beach_Team> Beach_Teams
 {
   get
   {
     var repo=LJR.DAL.Activities.Beach.Beach_Team.GetRepo();
     return from items in repo.GetAll()
          where items.TeamID == _TeamIDfk
          select items;
   }
 }

#endregion

It’s not ok for my use, I want a property returning only one “Beach_Team” object, so I can use it in my presentation layer with databound controls with no efforts

<asp:ListView runat="server" ID="LastTeams" DataSourceID="registrationsOds">
   <ItemTemplate>
     <%# Eval("Beach_Team.Name")%>
   </ItemTemplate>
 </asp:ListView>

So, what can I do ?

I can edit the ActiveRecord.cs file and add a property called “Beach_Team” and returning a Beach_Team object

 public Beach_Team Beach_Team
 {
   get
   {
     var repo = LJR.DAL.Activities.Beach.Beach_Team.GetRepo();
    return repo.GetByKey(_TeamIDfk);
   }
 }

It do the job !

But if I want to regenerate the ActiveRecord.cs file, my modification will be lost !

So the good solution is to modify the T4 template to generate this property.

Let’s do this :

Go to the ActiveRecord.tt file and search for ‘ Foreign Keys ‘.

Once you have find it look how it works:


#region ' Foreign Keys '
<#
 List<string> fkCreated = new List<string>();
 foreach(FKTable fk in tbl.FKTables)
 {
   if(IsTableOkToBeIncluded(fk.OtherTable))
   {
    string propName=fk.OtherQueryable;
    if(fkCreated.Contains(propName))
    {
      propName=fk.OtherQueryable+fkCreated.Count.ToString();
    }
    fkCreated.Add(fk.OtherQueryable);
#>
   public IQueryable<<#=fk.OtherClass #>> <#=propName #>
  {
  get
  {
     var repo=<#=Namespace #>.<#=fk.OtherClass#>.GetRepo();
     return from items in repo.GetAll()
         where items.<#=CleanUp(fk.OtherColumn)#> == _<#=CleanUp(fk.ThisColumn)#>
         select items;
   }
 }

<#
 }
 }
#>
 #endregion

In a nutshell, for each tables linked to the table we are on, we check if the access code to the table has to be generated (see my previous article : Subsonic : Specify the tables you really need ! ).

If the code has to be generated, we create a string variable named “propName” which will be the name of the property.

If this property already exists, we suffix the name to be sure we don’t have the same property twice.

Then we generate the IQueryable property.

Now, the first thing we want is to have the property name equals to the object name.

We could think that we don’t have to twist our minds because  we already have it with “fk.OtherClass” and i could be like that

 public <#=fk.OtherClass #> <#=fk.OtherClass #>
 {
  get
  {
   var repo=<#=Namespace #>.<#=fk.OtherClass#>.GetRepo();
   return repo.GetByKey(_<#=CleanUp(fk.ThisColumn)#>);
  }
 }

But what if we have two foreign keys to the same table ? We will have the same property twice! and we have to avoid it!

So, we have to check if this property isn’t already generated.

To do this, we can use the list “fkCreated” already present in the code, and create a “secondPropName” string variable, assign it the value of the OtherClass and check if it’s not already created :

 string secondPropName=fk.OtherClass;
 if(fkCreated.Contains(secondPropName))
 {
   secondPropName+=fkCreated.Count.ToString();
 }
 fkCreated.Add(secondPropName);

There is the final code


#region ' Foreign Keys '
<#
 List<string> fkCreated = new List<string>();
 foreach(FKTable fk in tbl.FKTables)
 {
   if(IsTableOkToBeIncluded(fk.OtherTable))
   {
    string propName=fk.OtherQueryable;
    if(fkCreated.Contains(propName))
    {
     propName=fk.OtherQueryable+fkCreated.Count.ToString();
    }
   fkCreated.Add(fk.OtherQueryable);

   string secondPropName=fk.OtherClass;
   if(fkCreated.Contains(secondPropName))
   {
     secondPropName+=fkCreated.Count.ToString();
   }
   fkCreated.Add(secondPropName);
#>
  public IQueryable<<#=fk.OtherClass #>> <#=propName #>
  {
    get
    {
      var repo=<#=Namespace #>.<#=fk.OtherClass#>.GetRepo();
      return from items in repo.GetAll()
        where items.<#=CleanUp(fk.OtherColumn)#> == _<#=CleanUp(fk.ThisColumn)#>
        select items;
    }
  }

  public <#=fk.OtherClass #> <#=secondPropName #>
  {
    get
    {
      var repo=<#=Namespace #>.<#=fk.OtherClass#>.GetRepo();
      return repo.GetByKey(_<#=CleanUp(fk.ThisColumn)#>);
    }
  }

<#
 }
 }
#>
 #endregion

Now I can use the linked object directly !

You can donwload my T4 templates here.

You can follow any responses to this entry through the RSS 2.0 feed.