Search code examples
asp.netforeign-keysdynamic-dataone-to-one

Asp.net Dynamic Data 4 One-to-One ForeignKey.ascx Returning Primary Key


I have a Dynamic Data 4 site using LinqToSql (.dbml) for 2 tables with a one-to-one relationship. One table (child) does not always have a row corresponding to the primary table.

Whenever that is the case (no child table row) instead of an empty row cell the DD ForeignKey.ascx Field template is displaying a link to:

Childtable/Detail.aspx?ChildPkField=PRIMARYTABLEGUID

I can't find any combination of SQL F-key setups and/or .dbml column property settings that prevent this. The only solution so far is a Custom ForeignKey.ascx template with

 protected string GetDisplayString()
    {
        object value = FieldValue;
        if (value == null)
        {
            //replace this
            //return FormatFieldValue(ForeignKeyColumn.GetForeignKeyString(Row));
            //with this
            return "";
        }
        else
        {
            return FormatFieldValue(ForeignKeyColumn.ParentTable.GetDisplayString(value));
        }
    }

Obviously not an optimal solution. To simplify I have removed all Custom Metadata for both tables and all other relationships the tables were involved in. No change.

I am sure it i something simple but I have looked at it for too long now - any help appreciated!

Here are the current Create Table Scripts from Sql Manager:

CREATE TABLE [dbo].[UserProfile](
[UserId] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
[ProfileUserName] [nvarchar](50) NOT NULL,
      CONSTRAINT [PK_Profile] PRIMARY KEY CLUSTERED ([UserId] ASC )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
 GO
 ALTER TABLE [dbo].[UserProfile] ADD  CONSTRAINT [DF_Profile_UserID]  DEFAULT (newid()) FOR [UserId]
GO

and

CREATE TABLE [dbo].[SubscribedUser](
[subUserId] [uniqueidentifier] NOT NULL,
[subExpireDate] [date] NULL,
     CONSTRAINT [PK_SubscribedUsers] PRIMARY KEY CLUSTERED ([subUserId] ASC) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) ON [PRIMARY]
 GO
ALTER TABLE [dbo].[SubscribedUser]  WITH CHECK ADD  CONSTRAINT [FK_SubscribedUser_UserProfile] FOREIGN KEY([subUserId]) REFERENCES [dbo].[UserProfile] ([UserId])
GO
ALTER TABLE [dbo].[SubscribedUser] CHECK CONSTRAINT [FK_SubscribedUser_UserProfile]
GO

Solution

  • In fact, the first method you used is perhaps the most optimal solution as the query has already been made on the server-side to determine whether the column data matches the foreign key relationship. No more querying is needed, just test FieldValue == null. Besides, the template controls are there to be edited, which is why they're not compiled to assemblies:

    protected string GetDisplayString()
    {
        object value = FieldValue;
        if (value == null)
        {
            //replace this
            //return FormatFieldValue(ForeignKeyColumn.GetForeignKeyString(Row));
            //with this
            return "";
        }
        else
        {
            return FormatFieldValue(ForeignKeyColumn.ParentTable.GetDisplayString(value));
        }
    }
    

    However, if you intend to preserved the value of the source field, use this method instead:

    protected string GetNavigateUrl()
    {
        //replace this
        //if (!AllowNavigation)
        //with this
        if (!AllowNavigation || FieldValue == null)
        {
            return null;
        }
        if (String.IsNullOrEmpty(NavigateUrl))
        {
            return ForeignKeyPath;
        }
        else
        {
            return BuildForeignKeyPath(NavigateUrl);
        }
    }
    

    Under certain conditions (including the default css styles), it will generate a hyperlink-looking field that cannot be clicked. You can use jQuery to filter and remove those false hyperlinks by setting their outerHTML as their innerHTML (client-side scripting). Of course, there are more elegant methods like adding a <asp:label> to the ForeignKey.ascx and switching to that control when needed.

    Note: this method works when using ADO.NET Entity Data Model. I didn't try it on LinqToSqlClasses.