Saturday 11 April 2009

Hiding Foreign Key column Globally in Dynamic Data

This article is based on a question in the Dynamic Data forum Hide Foreign Key Column. And so I thought I’d document what I did for posterity or at least so I can find it again if ever the question arises again :D

So I decided  here is what I would need:

  1. An Attribute to mark FK relation ships as hidden.
  2. Some Extension methods to extract and test the attribute
  3. An IAutoFieldGenerator to filter the Columns on a page

The Attribute

[AttributeUsage(AttributeTargets.Class)]
public class HideFKColumnAttribute : Attribute
{
    public Boolean Hidden { get; set; }
    public HideFKColumnAttribute()
    {
        Hidden = false;
    }

    public HideFKColumnAttribute(Boolean hide)
    {
        Hidden = hide;
    }
}

Listing 1 – HideFKColumnAttribute

This attribute will be set to true if the FK relationship is to be hidden and will default to false to make testing for easy.

The Extension Methods

/// <summary>
/// Test if this FK column should be hidden.
/// </summary>
/// <param name="column">
/// The column to test.
/// </param>
/// <returns>
/// Returns true if it the column should be hidden or false if not.
/// </returns>
public static Boolean FkIsHidden(this MetaColumn column)
{
    var fkColumn = column as MetaForeignKeyColumn;
    if (fkColumn != null)
        return fkColumn.ParentTable.GetAttributeOrDefault<HideFKColumnAttribute>().Hidden;
    else
        return false;
}

/// <summary>
/// Get the attribute or a default instance of the attribute
/// if the Table attribute do not contain the attribute
/// </summary>
/// <typeparam name="T">Attribute type</typeparam>
/// <param name="table">Table to search for the attribute on.</param>
/// <returns>The found attribute or a default instance of the attribute of type T</returns>
public static T GetAttributeOrDefault<T>(this MetaTable table) where T : Attribute, new()
{
    return table.Attributes.OfType<T>().DefaultIfEmpty(new T()).FirstOrDefault();
}

Listing 2 – Extension methods

FkIsHidden is used to test if the ParentTable has the HideFKColumnAttribute applied. And the extension method GetAttributeOrDefault returns an HideFKColumnAttribute which is false if not set but true if set explicitly.

The IAutoFieldGenerator class

using System.Collections;
using System.Collections.Generic;
using System.Web.DynamicData;
using System.Web.UI;

public class HideColumnFieldsManager : IAutoFieldGenerator
{
    protected MetaTable _table;
    protected PageTemplate _currentPage;

    public HideColumnFieldsManager(MetaTable table, PageTemplate currentPage)
    {
        _table = table;
        _currentPage = currentPage;
    }

    public ICollection GenerateFields(Control control)
    {
        var oFields = new List<DynamicField>();

        foreach (var column in _table.Columns)
        {
            // carry on the loop at the next column  
            // if scaffold table is set to false or DenyRead
            if (!column.Scaffold ||
                column.IsLongString ||
                column.FkIsHidden())
                continue;

            var f = new DynamicField();

            f.DataField = column.Name;
            oFields.Add(f);
        }
        return oFields;
    }
}

Listing 3 – HideColumnFieldManager

This is the same IAutoFieldGenerator class as used in the article Dynamic Data - Hiding Columns in selected PageTemplates only I’ve added an column.FkIsHidden() to the list of tests for column to display.

I think that about wraps this up, you can download the file from the article mentioned in the paragraph above and then add the extension methods and attribute. Then modify the HideColumnFieldManager class to test for the Hidden FK relationship.

Download

17 comments:

Anonymous said...

Hi Steve,
I'm working on a dynamic data project.
Requirement:

Table: Customer
Column1:ReviewDate (datetime)
Column2:Review (boolean)

Rules:

1)By default "ReviewDate" column hidden or readonly in "Edit" mode.
2)When click "Review" checkbox ticked i want to show the "ReviewDate" column with editable text field to enter datetime.
3)once i enter the value in "ReviewDate" field i want "ReviewDate" and "Review" fileds to be readonly.

I tried different work arounds but no luck.

Please give me one example code to accomplish this task.

Anonymous said...

Hei...can I have sample project to download so that I can place the code in correct place to make this thing work.I am new to DD and need to hide FK column from my pages globally.

Stephen J. Naughton said...

Sure just drop me an e-mail

Steve :D

Stephen J. Naughton said...

I just added a download for the project.

Steve :D

Anonymous said...

Wow...that is quick response...I was working on that and you just uploaded the files.Thanks I will be always grateful to you if that works..let me try now.

Thanks in Advance:)
Ashfaq

Anonymous said...

Hei...I couldn't find out the way yet, from where I should set the property FkIsHidden, I need to remove/hide all the FK column in my ListDetails.aspx/or List.aspx page:(.

Ashfaq

Stephen J. Naughton said...

If you open the project and look in the App_Code\NW.Metadata.cs file you can see it in use there. You apply the attribute to the metadata classes, see this http://www.asp.net/learn/3.5-SP1/video-291.aspx for more info on setting up metadata.

Steve :D

Anonymous said...

Thanks for your quick response, I saw your NW.Metadata.cs class and saw there [HideFKColumn(true)] attribute is set. So according to my understanding, when I will click on Employee Table in DD Grid, the Employees List page will Populate all column except the relational column[EmployeeTerritories, Orders, Employees]. But it is still populating those Relational column, I watched the Video link and exclude your Metadata.cs class and made my own partial Class named "Employee" and set [HideFKColumn(true)] to that for testing, but that didn't work also...still showing Relational Column for Employees.What I am missing?

I want a generic way to turn off Relational Column from populating for all Tables. If I have to make partial Class for all the Tables that will be little time consuming for my purpose.

Thanks for your Time

Ashfaq

Stephen J. Naughton said...

I Ashfaq, if you look at the NW.Metadata.cs file and the entity it is on it means where ever a product exists it will hide FK to that table instead of hiding using [ScaffoldColumn(false)] each place it occurs.

So you put
[ScaffoldColumn(false)]
on the FK column and you put
[HideFKColumn(true)]
on the FK Table

does that make sense now?

Steve :D

P.S. you can always use Digsby on this page to chat to me if you like when I'm online :D

Ashfaq said...

Sorry...I felt asleep.What I wanted to say is that When I put [ScaffoldColumn(false)] on Order object on your class, it was still showing Order Relational Column on Employee Table. But if I arrange your class like

[MetadataType(typeof(EmployeeMM))]
Public partial class Employee
{
}

Public class EmployeeMM
{
[ScaffoldColumn(false)]
Public object order{get;set;}
}
it was not showing Order Relational column(which is desired), So My question is why Order column was showing when I put [ScaffoldColumn(false)] on your class. And also I made a class Order in my way and set [HideFKColumn(true)] on that class, it didn't work. And finally...it seems that to Hide FK column I have to make individual class and set [HideFKColumn(true)] for those class, but is there any way so that I can set [HideFKColumn(true)] to any place for once and that will stop showing all FK column in all Table. I just need to Hide all FK column from All Table? I have just woke up and typing without my glass even..so sorry If I make any typing mistake:).

Stephen J. Naughton said...

Hi Ashfaq, tht's real weired because that's the recomended way nesting the classes. Have a look at those created byt DomainService.

Steve :D

Unknown said...

Great post! I am new to DD, and your blog has been in-valuable to me in learning about what is going on under the hood. So thanks! I have the FK hidden in the grid using the code listing and it works as expected, but the FK is still being auto generated as a dynamic filter which I don’t want either. Can you point me in the right direction for handling this issue?

Stephen J. Naughton said...

Hi I think you want to use this post here for limiting which filters are shown on a page: http://csharpbits.notaclue.net/2008/05/dynamicdata-limit-filter-fields.html
This is for .Net 3.5 SP1 DD not Preview 4 or VS2010 Beta 1 I will have a look at doing a version for Beta 1 if there is any interest.

Steve :D

Unknown said...

Is this functionality built into Preview 4 since the options exists to add different column to the filters. Would you recommend just starting with preview 4 since I am just starting out.

Stephen J. Naughton said...

I do not believe it is or in VS2010 and .Net 4.0 Beta 1 either. However this should work without problem in the Preview 4 and Beta 1.

Steve :D

Waqar Farooq Janjua said...

Steve I have a problem.
I want to show a data column instead of foreign key in dynamic data site. How can I achieve this ?
Please Help me

Stephen J. Naughton said...

Hi Waqar, not sure what you want here please email me direct and I will see if I can help.

Steve