7

I have two entities:

public class ParentThing
{
    [Key]
    public int Id { get; set; }

    [Required]
    public ChildThing TheFirstThing { get; set; }

    public ChildThing TheSecondThing { get; set; }
}

public class ChildThing
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string Code { get; set; }

    public string Name { get; set; }
}

and a view model:

public class ParentViewModel
{
    public string Message { get; set; }

    public ParentThing ParentThing { get; set; }
}

and a view:

@using (@Html.BeginForm())
{
    <label>Code 1</label>
    @Html.EditorFor(model => model.ParentThing.TheFirstThing.Code)

    <label>Name 1</label>
    @Html.EditorFor(model => model.ParentThing.TheFirstThing.Name)

    <label>Code 2</label>
    @Html.EditorFor(model => model.ParentThing.TheSecondThing.Code)

    <label>Name 2</label>
    @Html.EditorFor(model => model.ParentThing.TheSecondThing.Name)

    <input type="submit" value="Save" />
}

In my post back I add the ParentThing to the context and attempt to save changes. I receive a validation error on the Code property of the TheSecondThing property of the ParentThing as it is required.

What are some alternatives for saving an optional property that contains required properties?

Jamie
  • 779
  • 1
  • 9
  • 17
  • The alternative is to remove the dependency on entity classes in your view models. You want to use Data Transfer Objects (DTOs). This http://stackoverflow.com/questions/5995140/models-viewmodels-dtos-in-mvc-3-application will help you get started. – Jasen Dec 09 '13 at 23:57

2 Answers2

0

As jamie suggested, remove any dependencies between your entities and your models...they're 2 separate things Don't use data annotations on your entities, use data annotations on your models. Remove the ParentThing property of your model and add as primitive properties in your model as you need (i.e. Message, ParentThingId, TheFirstThingId, TheFirstThingCode, TheFirstThingName, etc) and add all your data annotation attributes to the model. If you need to validate your entities (you probably will) do that on your business logic.

I hope it makes sense

Leo.

Leo
  • 14,625
  • 2
  • 37
  • 55
0

In response to the current suggestions this is what I now have.

Entities remain the same.

The modified view model:

public class ParentViewModel
{
    public string Message { get; set; }

    public string FirstThingCode { get; set; }

    public string FirstThingName { get; set; }

    public string SecondThingCode { get; set; }

    public string SecondThingName { get; set; }
}

The modified view:

@using (@Html.BeginForm())
{
    <label>Code 1</label>
    @Html.EditorFor(model => model.FirstThingCode)

    <label>Name 1</label>
    @Html.EditorFor(model => model.FirstThingName)

    <label>Code 2</label>
    @Html.EditorFor(model => model.SecondThingCode)

    <label>Name 2</label>
    @Html.EditorFor(model => model.SecondThingName)

    <input type="submit" value="Save" />
}

The DTO:

public class ParentThingDTO
{
    public ParentThingDTO(ParentViewModel model)
    {
        FirstThingCode = model.FirstThingCode;
        FirstThingName = model.FirstThingName;
        SecondThingCode = model.SecondThingCode;
        SecondThingName = model.SecondThingName;
    }

    public int Id { get; set; }

    public string FirstThingCode { get; set; }

    public string FirstThingName { get; set; }

    public string SecondThingCode { get; set; }

    public string SecondThingName { get; set; }

    public ParentThing GenerateEntity()
    {
        var thing = new ParentThing();
        thing.TheFirstThing = new ChildThing
        {
            Code = FirstThingCode,
            Name = FirstThingName
        };

        if (!string.IsNullOrWhiteSpace(SecondThingCode))
        {
            thing.TheSecondThing = new ChildThing
            {
                Code = SecondThingCode,
                Name = SecondThingName
            };
        }

        return thing;
    }
}

The postback action in the controller:

    [HttpPost]
    public ActionResult Create(ParentViewModel model)
    {
        try
        {
            var dto = new ParentThingDTO(model);
            var parentThing = dto.GenerateEntity();

            using (var context = new QuantumContext())
            {
                context.ParentThings.Add(parentThing);
                context.SaveChanges();
                model.Message = "Saved";
            }
        }
        catch (Exception ex)
        {
            model.Message = ex.Message;
        }

        return View(model);
    }

The null or whitespace test in the dto GenerateEntity method solves my initial problem of the MVC required property within the optional property. How does it look?

Jamie
  • 779
  • 1
  • 9
  • 17
  • When editing a row, instead of generating a new `ParentThing` you want to look up and retrieve the `ParentThing` instance (by id). Then edit the entity values based on the new values coming in from your `ParentThingDTO`. On creation you won't have an existing row so it's safe to create a new entity as you have done. – Jasen Dec 11 '13 at 02:08