Sunday, 7 December 2008

Dynamic Data – Registering Multiple Models

There are two way to get multiple model into your site:

  1. Register Multiple DataContexts with the default Model.
  2. Register each DataContext with it’s own Model.

My Models:

ScreenShot149

Register Multiple DataContexts with the default Model.

This is the simplest approach you have multiple EDMX or DBML files, all you need to do is lines like so in the Global.ascx file:

model.RegisterContext(typeof(Table1DataContext), 
    new ContextConfiguration() { ScaffoldAllTables = true });
model.RegisterContext(typeof(Table2DataContext), 
    new ContextConfiguration() { ScaffoldAllTables = true });

Listing 1 – Adding a Context to the Model

Note: you are adding the DataContext/ObjectContext to the existing model

ScreenShot148

Figure 1 – Both Contexts in one model.

Register each DataContext with it’s own Model.

This is slightly more work, you are basically duplicating each line of code for one model to two.

public static void RegisterRoutes(RouteCollection routes)
{
    // Model1 ======================================================
    MetaModel model = new MetaModel();

    model.RegisterContext(typeof(Table1DataContext),
         new ContextConfiguration() { ScaffoldAllTables = true });

    routes.Add(new DynamicDataRoute("{table}/{action}.aspx")
    {
        Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
        Model = model
    });

    // Model2 ======================================================
    MetaModel model1 = new MetaModel();

    model1.RegisterContext(typeof(Table2DataContext),
        new ContextConfiguration() { ScaffoldAllTables = true });

    routes.Add(new DynamicDataRoute("Model1/{table}/{action}.aspx")
    {
        Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
        Model = model1
    });
}

Listing 2 – Creating multiple models

Note: See the DynamicDataRoute("Model1/{table}/{action}.aspx") it is important that you differentiate the route in some way especially if some of the tables are the same name.

ScreenShot150

Figure 2  - Model route

 ScreenShot151

Figure 3  - Model1 route

As you can see from Figures 1 & 2 just by adding a differentiating route you can guarantee that there will be no table name conflicts between you models.

That get your Models registered now how to access them:

<h2>My first set of tables</h2>

<br /><br />

<asp:GridView ID="Menu1" runat="server" AutoGenerateColumns="false"
    CssClass="gridview" AlternatingRowStyle-CssClass="even">
    <Columns>
        <asp:TemplateField HeaderText="Table Name" SortExpression="TableName">
            <ItemTemplate>
                <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='<%#Eval("ListActionPath") %>'><%#Eval("DisplayName") %></asp:HyperLink>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
<br /><br />

<h2>My Second set of tables</h2>

<br /><br />

<asp:GridView ID="Menu2" runat="server" AutoGenerateColumns="false"
    CssClass="gridview" AlternatingRowStyle-CssClass="even">
    <Columns>
        <asp:TemplateField HeaderText="Table Name" SortExpression="TableName">
            <ItemTemplate>
                <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='<%#Eval("ListActionPath") %>'><%#Eval("DisplayName") %></asp:HyperLink>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Listing 3 – Add a second Menu to the Default.aspx

protected void Page_Load(object sender, EventArgs e)
{
    // Model 1
    System.Collections.IList visibleTables = 
MetaModel.Default.VisibleTables;
if (visibleTables.Count == 0) { throw new InvalidOperationException("There are no accessible tables."); } Menu1.DataSource = visibleTables; Menu1.DataBind(); // Model 2 System.Collections.IList visibleTables2 =
MetaModel.GetModel(typeof(Table2DataContext)).VisibleTables;
if (visibleTables.Count == 0) { throw new InvalidOperationException("There are no accessible tables."); } Menu2.DataSource = visibleTables2; Menu2.DataBind(); }

Listing 4 – Default.aspx.cs code behind

Model 1 is normal but Model 2 you are required to get the model using GetModel(typeof(DataContextName)) so what you should now see when you run the site is:

ScreenShot147

Figure 2 – Both Models shown


I know this is simple stuff and I know I have answered questions be for butI found I hadn’t an article on my blog smile_teeth

And here’s the download

12 comments:

Anonymous said...

Hi

Can you help me?
if I want to view in default.aspx page only 1, create a new aspx page with 2 button (1 for TableS1 and 1 for TableS2) and a redirect to default.aspx?
It´s possible?
bye

Stephen J. Naughton said...

Yes possible indeed, bu I would not even have multiple pages I would surround the two gridview with a pannel and make the pannel visible when you click the relavent button :)

Steve :D

Anonymous said...

I have multiple databases that share the same DataContext and I need to switch between them. I can see no way to do this, is there any way to reset the MetaModel.

I noticed in the stack trace there is some dictionary that contains the Registered DataContexts. What I need is to be able to reset this dictionary, why cant we do this?!

Thanks.

Stephen J. Naughton said...

Sorry I've never had a need for this sort of thing so I have never had to create a solution.

Steve :(

Alexander said...

Thanks a lot.....very useful article....It solved my problem.........thanks

Stephen J. Naughton said...

Thanks it does encourage me :)

Alexander said...

Hi Steve!!

Need help regarding Foreign Key relationship in Dynamic Data [Linq to Sql] project.

I have two tables : Dealer & DealerVendor and the relationShip is set on DealerId(One to Many) and not on ID.

the structure of tables are :

Table Dealer :
Id as Int Primary
DealerID as string,
Description as string

Table DealerVendor :
Id as int primary,
DealerID as string

my problem is that, that when i click on Dealer Table it displays the DealerVendor column but when i click on DealerVendor link i got an error
"Operator '==' incompatible with operand types 'String' and 'Int32' "

because the List.aspx shows the querystring of ID column not the DealerID.....

I want that it should display the DealerId link so that DealerVendor table can be sorted on "DealerId"...

Please help me in this matter.

Appreciate for quick help.

Stephen J. Naughton said...

You cannot have that kind of relationship it must be PK->FK not FK->FK

Alexander said...

Steve, It is not a matter of PK-FK but it a matter of relationship based on "string" and not on "int"...

Please see the data....relationship is set on "DealerID"....Primary key is "DealerID" of Dealer table.

Dealer
--------------------
ID -- DealerID(PK)
--------------------
1 -- DMIT
2 -- DMOT
3 -- DMEM
4 -- DMER

Table DealerVendor
--------------------
Id -- DealerId(FK)
--------------------
1 -- DMIT
2 -- DMIT
3 -- DMEM
4 -- DMIT
5 -- DMEM
6 -- DMOT
7 -- DMOT

Now....please help me in this scenario....

Stephen J. Naughton said...

Hi can you send me your schema, so I can build the DB structure and test it please, my e-mail is in the top right of my blog.

Steve.

Anonymous said...

Hy Steve,

I have a problem I can't fix since days. I have a Dynamic Data project with Entity Framework 5. I try to register multiple DbContexts. I always get the error: "The context type ... is not supported.". When I try the trick with IObjectContextAdapter to use the ObjectContext it works only for the first Model. Then I get a duplicated key violation. On my search I found the DynamicData.EFCodeFirstProvider. When I use it, I get the same error again "The context type 'DynamicData.EFCodeFirstProvider.EFCodeFirstDataModelProvider' is not supported". What am I making wrong? Please help me,

Stefan

Stephen J. Naughton said...

Hi Stefan, you will get more mileage on the forum but What I do is create a collection of models send me an e-mail direct and I will send you the code I use.

Steve
P.S. my e-mail is in the top right on my blog :)