1

I have a table called Field in my database and a table called FieldValue. My models look like this:

[Table("Field")]
public class Field
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int FieldId { get; set; }
    public string Value { get; set; }
}

[Table("FieldValue")]
public class FieldValue
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int FieldValueId { get; set; }
    public int FieldId { get; set; }
    public string Value { get; set; }
}

I would like to display each field as the label and then a textbox for each FieldValue associated with that Field but I'm not quite sure how to approach this. I am using MVC 4 if that matters.

jrummell
  • 42,637
  • 17
  • 112
  • 171
Pittfall
  • 2,751
  • 6
  • 32
  • 61
  • Can I ask why you have chosen this structure? By the way you have phrased the question it seems like you have a 1-1 relationship between fields and values? – beyond-code Dec 07 '12 at 16:19
  • You ask a good question and I just didn't want to make question too long and my table is actually missing some columns but I kept it simple for now. I am building a page with Fields and I can have multiple pages with the same Fields but obviously the values can be different – Pittfall Dec 07 '12 at 16:25

3 Answers3

1

You could return the joined array to a view model class (untested for syntax/typos):

public class ViewModel
{
    public KeyValuePair<string,string> Fields { get; set; }
}

var kvps = Fields.Join(FieldValues,
    f => f.FieldId,
    fv => fv.FieldId,
    (f, fv) => 
        new KeyValuePair<string,object>(f.Value, fv.Value)
    }).ToArray();
var viewModel = new ViewModel { Fields = kvps };

return View(viewModel);

Then in the view, simply iterate over the values:

@model MyNamespace.ViewModel

@foreach (var kvp in Model.Fields)
{
    <span class="field-label">@kvp.Key</span>
    <span class="field-value">@Html.TextBoxFor(_ => kvp.Value)</span>
}

If you put this in a form, then the postback should also work correctly (since it uses TextBoxFor).

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • This seems like the right answer. The only problem is that I get an exception that says "Only parameterless constructors and initializers are supported in LINQ to Entities." Which I assume has something to do with the KeyValuePair but was wondering if you knew a work around. – Pittfall Dec 10 '12 at 14:41
  • 1
    The correct query: `var kvps = (from f in FieldDb.Field join fv in FieldDb.FieldValue on f.FieldId equals fv.FieldId into JoinedFieldId from fv in JoinedFieldId.DefaultIfEmpty() select new { Key = f.FieldId, Value = fv.Value }).ToList().Select(x=>new KeyValuePair(x.Key, x.Value));` You were on the right track. – Pittfall Dec 10 '12 at 16:33
0

Not sure what you are trying to do and i dont want to make any assumptions but i have a feeling you may be going the wrong way. MVC as you probably Know has strongly typed views so that you can "bind" a view to a useful business object such as Person and then produce HTML by using something like

Html.TextBoxFor(p => p.Name)

where p is a Person

That thing you are trying to build seems to be a hack to achieve this. Cant you just create meaningful objects that hold your information and then pass them to strongly typed views? If another page has the same fields then pass the same object.

  • I am aware of the HTML helpers with razor. and I know how to do `@Html.DisplayFor(f => f.Value)` and `Html.TextBoxFor(f => f.Value)` but I am not sure how to do `@HTML.DisplayFor(f => f.Value)` and `Html.TextBoxFor(fv => fv.Value)`. I don't really know how to pass all of that to the view – Pittfall Dec 07 '12 at 16:49
  • not sure that i get this, if you want to pass both f and fv to the view for some reason then why dont you wrap them in a new object "wrap" and do HTML.DisplayFor(wrap => wrap.f.Value) and Html.TextBoxFor(wrap => wrap.fv.Value). I cant see how else this could be possible – user1568656 Dec 07 '12 at 16:53
0

The answer here depends on whether or not you will know how many fields you have up front.

If you do, then you should create a strongly typed model to bind your mvc view to, and use data annotations as follows:

public class MyModel
{
    [Display(Name="Field name 1")
    public string Field1 { get; set; }

    [Display(Name="Field name 2")
    public string Field2 { get; set; }
}

Then in your view you can use the following code:

@Html.LabelFor(model => model.Field1)
@Html.TextBoxFor(model => model.Field1)

@Html.LabelFor(model => model.Field1)
@Html.TextBoxFor(model => model.Field1)

If however you don't know the number of fields up front and therefore can't populate a strongly typed model like this, then I would advise you do the following:

  • Add a navigation property to Field so you can get at the Value easily
  • Write some code to map between the database to perhaps a dictionary of
  • Write a loop in your view and use the old school html helpers like this:

...

@for (var key in model.FieldDictionary.Keys)
{
    @Html.Label("key", model.FieldDictionary(key))
    @Html.TextBox(model.FieldDictionary(key))
}

Please note this code is not tested, and in the second example you will have to figure out how to get the data back in when you post the form, but hopefully should give you a starting point.

Hope this helps!

beyond-code
  • 1,423
  • 1
  • 12
  • 20