MVC View Model constraints
(Created page with "I made a little MVC sample. I deliberately made it really small and simple – but made sure it shows create,delete and update of attributes and relations. File:01 - VM cons...")
 
(Automatically adding template at the end of the page.)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
I made a little MVC sample. I deliberately made it really small and simple – but made sure it shows create,delete and update of attributes and relations.
I made a little MVC sample. I deliberately made it small and simple – but ensured it shows the creation, deletion, and updating of attributes and relations.
[[File:01 - VM constraints.png|none|thumb|318x318px]]
[[File:01 - VM constraints.png|none|thumb|318x318px]]


Line 5: Line 5:


Three ViewModels:
Three ViewModels:
* One to act as an index page:
[[File:02 - VM constraints.png|none|thumb|447x447px]]
* One that shows Example1 and its connected Example2’s:
[[File:03 - VM constraints.png|none|thumb|460x460px]]
* And one that shows a single Example2:
[[File:04 - VM constraints.png|none|thumb|486x486px]]
Then, I made 2 controllers - one for Example1 and one for Example2.
The main goal of using the model-defined ViewModels is to use as little code as possible in the Views and in the Controllers and to also make the code as predictable, simple, and easy to maintain as possible.
The patterns that emerge appear like this:
'''1. The SHOW STUFF:'''
<html>


One to act as index page
<pre class="code">        <span style="background: white; color: blue;">public </span><span style="background: white; color: #2b91af;">ActionResult </span><span style="background: white; color: black;">Details(</span><span style="background: white; color: blue;">string </span><span style="background: white; color: black;">Identity)
[[File:02 - VM constraints.png|none|thumb|447x447px]]
        {
          EcoProject1.</span><span style="background: white; color: #2b91af;">Example2 </span><span style="background: white; color: black;">e2 = </span><span style="background: white; color: blue;">this</span><span style="background: white; color: black;">.EcoSpace.ExternalIds.ObjectForId(Identity).AsObject </span><span style="background: white; color: blue;">as </span><span style="background: white; color: black;">EcoProject1.</span><span style="background: white; color: #2b91af;">Example2</span><span style="background: white; color: black;">;
          </span><span style="background: white; color: blue;">return </span><span style="background: white; color: black;">View(</span><span style="background: white; color: #a31515;">"Details"</span><span style="background: white; color: black;">,  </span><span style="background: white; color: #2b91af;">VMExample2</span><span style="background: white; color: black;">.Create(</span><span style="background: white; color: blue;">this</span><span style="background: white; color: black;">.EcoSpace, e2));
        }
</span></pre>


one that shows Example1 and its connected Example2’s
</html>
[[File:03 - VM constraints.png|none|thumb|460x460px]]


and one that show a single Example2
Here we resolve an id to an object, then use the ViewModel as a façade on that object and send it to the view. The benefit is that I do not have to expose my whole business object to the view, and I can also do view specific derivations in the ViewModel.
[[File:04 - VM constraints.png|none|thumb|486x486px]]


Then I made 2 controllers, one for Example1 and one for Example2.
'''2. The UPDATE STUFF:'''


The main goal with using the model defined ViewModels is to use as little code as possible in the Views and in the Controllers. And also to make the code used predictable, simple and easy to maintain.
<html>


The patterns that emerges is sort of this:
<pre class="code"><span style="background: white; color: black;">        [</span><span style="background: white; color: #2b91af;">HttpPost</span><span style="background: white; color: black;">]
        </span><span style="background: white; color: blue;">public </span><span style="background: white; color: #2b91af;">ActionResult </span><span style="background: white; color: black;">Details(</span><span style="background: white; color: blue;">string </span><span style="background: white; color: black;">Identity, </span><span style="background: white; color: #2b91af;">VMExample2 </span><span style="background: white; color: black;">offlinevm)
        {
          </span><span style="background: white; color: #2b91af;">Example2 </span><span style="background: white; color: black;">e2 = EcoSpace.ExternalIds.ObjectForId(Identity).AsObject </span><span style="background: white; color: blue;">as </span><span style="background: white; color: #2b91af;">Example2</span><span style="background: white; color: black;">;
          </span><span style="background: white; color: blue;">try
          </span><span style="background: white; color: black;">{
            </span><span style="background: white; color: #2b91af;">VMExample2 </span><span style="background: white; color: black;">onlinevm=</span><span style="background: white; color: #2b91af;">VMExample2</span><span style="background: white; color: black;">.Create(EcoSpace, e2);
            </span><span style="background: white; color: #2b91af;">ViewModelHelper</span><span style="background: white; color: black;">.ApplyValues(offlinevm, onlinevm, </span><span style="background: white; color: blue;">null</span><span style="background: white; color: black;">);
            Commit();
            </span><span style="background: white; color: blue;">return </span><span style="background: white; color: black;">View(</span><span style="background: white; color: #a31515;">"Details"</span><span style="background: white; color: black;">, onlinevm);
          }
          </span><span style="background: white; color: blue;">catch
          </span><span style="background: white; color: black;">{
            </span><span style="background: white; color: blue;">return </span><span style="background: white; color: black;">View(</span><span style="background: white; color: #a31515;">"Details"</span><span style="background: white; color: black;">, offlinevm);
          }
        }


1. The SHOW STUFF:
</span></pre>
</html>


jhgftykjbhf
Here we resolve the id to an object, create an online ViewModel for that object, apply the values from the sent-in offline ViewModel, and commit. The benefits here are mainly that only the things in the ViewModel can change, protecting the rest of your business object. The ViewModel can also be a compilation of details from other parts of your model – and the view need not know anything about this. The ViewModel also has the ability to opt-out of the constraints from the seen classes that do not make sense in this view. This way, class-wide constraints can be used and filtered per view.


Here we resolve an id to an object, then use the ViewModel as a façade on that object and send it to the view. The benefit is that I do not have to expose my whole business object to the view, and I can also do view specific derivations in the viewmodel.
'''3. The DELETE STUFF:'''


2. The UPDATE STUFF:
<html>


kjhjghj
<pre class="code">        <span style="background: white; color: blue;">public </span><span style="background: white; color: #2b91af;">ActionResult </span><span style="background: white; color: black;">Delete(</span><span style="background: white; color: blue;">string </span><span style="background: white; color: black;">Identity)
        {
          </span><span style="background: white; color: #2b91af;">Example1 </span><span style="background: white; color: black;">e1 = </span><span style="background: white; color: blue;">this</span><span style="background: white; color: black;">.EcoSpace.ExternalIds.ObjectForId(Identity).AsObject </span><span style="background: white; color: blue;">as </span><span style="background: white; color: #2b91af;">Example1</span><span style="background: white; color: black;">;
          BusinessDelete(e1);
          Commit();
          </span><span style="background: white; color: blue;">return </span><span style="background: white; color: black;">View(</span><span style="background: white; color: #a31515;">"Index"</span><span style="background: white; color: black;">, </span><span style="background: white; color: #2b91af;">VMExample1Index</span><span style="background: white; color: black;">.Create(</span><span style="background: white; color: blue;">this</span><span style="background: white; color: black;">.EcoSpace, </span><span style="background: white; color: blue;">null</span><span style="background: white; color: black;">));
        }
</span></pre>


Here we resolve the id to an object, create an online viewmodel for that object, apply the values from the sent in offline viewmodel and commit. The benefits here are mainly that only the things in the viewmodel can change –protecting the rest of your business object. The viewmodel can also be a compilation of details from other parts of your model – and the view need not know anything about this. The viewmodel also has the ability to opt out the constraints from the seen classes that does not make sense in this view. This way class wide constraints can be used and filtered per view.
</html>


3. The DELETE STUFF:
Again, look up the object from the id, call the BusinessDelete that checks our delete constraints, then prepare a ViewModel for where we should end up afterward. The benefits here are that constraints that are put in place to protect the information from ending up in any illegal state are effectively applied even if the update touches several objects.


jjklpkmn
'''4. The CREATE STUFF:'''


Again, look up object from id, call the BusinessDelete that checks our delete constraints, then prepare a viewmodel for where we should end up afterwards. The benefits here are that constraints that are put in place to protect the information from ending up in any illegal state is effectively applied even if the update touches several objects.
<html>


4. The CREATE STUFF:
<pre class="code">        <span style="background: white; color: blue;">public </span><span style="background: white; color: #2b91af;">ActionResult </span><span style="background: white; color: black;">Create()
        {
          </span><span style="background: white; color: #2b91af;">Example1 </span><span style="background: white; color: black;">x1 = </span><span style="background: white; color: blue;">new </span><span style="background: white; color: #2b91af;">Example1</span><span style="background: white; color: black;">(</span><span style="background: white; color: blue;">this</span><span style="background: white; color: black;">.EcoSpace) { Attribute1=</span><span style="background: white; color: #a31515;">"HoHoHo"</span><span style="background: white; color: black;">,Attribute2=</span><span style="background: white; color: #a31515;">"HeHeHe"</span><span style="background: white; color: black;">};
          CommitSkipValidate();
          </span><span style="background: white; color: blue;">return </span><span style="background: white; color: black;">View(</span><span style="background: white; color: #a31515;">"Index"</span><span style="background: white; color: black;">,</span><span style="background: white; color: #2b91af;">VMExample1Index</span><span style="background: white; color: black;">.Create(</span><span style="background: white; color: blue;">this</span><span style="background: white; color: black;">.EcoSpace, </span><span style="background: white; color: blue;">null</span><span style="background: white; color: black;">));
        }
</span></pre>


nkjggyl
</html>


Just create the object and commit, skipping the Validate – since I do not know how to react to objects that break their constraints at birth…
Just create the object and commit, skipping the Validate – since I do not know how to react to objects that break their constraints at birth…


The Views use the ViewModels and get code completion since the viewmodels has generated code:
The Views use the ViewModels and get code completion since the ViewModels have generated code:
 
<html>
<pre class="code"><span style="background: white; color: blue;">&lt;</span><span style="background: white; color: maroon;">table</span><span style="background: white; color: blue;">&gt;
</span><span style="background: yellow; color: black;">@</span><span style="background: white; color: blue;">foreach </span><span style="background: white; color: black;">(</span><span style="background: white; color: blue;">var </span><span style="background: white; color: black;">item </span><span style="background: white; color: blue;">in </span><span style="background: white; color: black;">Model.Example2) {
    </span><span style="background: white; color: blue;">&lt;</span><span style="background: white; color: maroon;">tr</span><span style="background: white; color: blue;">&gt;
        &lt;</span><span style="background: white; color: maroon;">td</span><span style="background: white; color: blue;">&gt;
            </span><span style="background: yellow; color: black;">@</span><span style="background: white; color: black;">Html.DisplayFor(modelItem =&gt; item.Attribute1)
        </span><span style="background: white; color: blue;">&lt;/</span><span style="background: white; color: maroon;">td</span><span style="background: white; color: blue;">&gt;
        &lt;</span><span style="background: white; color: maroon;">td</span><span style="background: white; color: blue;">&gt;
            </span><span style="background: yellow; color: black;">@</span><span style="background: white; color: black;">Html.ActionLink(</span><span style="background: white; color: #a31515;">"Details &amp; Edit"</span><span style="background: white; color: black;">, </span><span style="background: white; color: #a31515;">"DetailsForExample2"</span><span style="background: white; color: black;">, </span><span style="background: white; color: blue;">new </span><span style="background: white; color: black;">{  Identity=item.Identity }) |
            </span><span style="background: yellow; color: black;">@</span><span style="background: white; color: black;">Html.ActionLink(</span><span style="background: white; color: #a31515;">"Delete"</span><span style="background: white; color: black;">, </span><span style="background: white; color: #a31515;">"DeleteExample2"</span><span style="background: white; color: black;">, </span><span style="background: white; color: blue;">new </span><span style="background: white; color: black;">{ Identity=item.Identity })
        </span><span style="background: white; color: blue;">&lt;/</span><span style="background: white; color: maroon;">td</span><span style="background: white; color: blue;">&gt;
    &lt;/</span><span style="background: white; color: maroon;">tr</span><span style="background: white; color: blue;">&gt;
</span><span style="background: white; color: black;">}
    </span><span style="background: white; color: blue;">&lt;/</span><span style="background: white; color: maroon;">table</span><span style="background: white; color: blue;">&gt;
</span></pre>
 
 
</html>
Having the bulk of your logic and your rules in the model is a good thing.


glkbknj
'''Additional Features'''


Having the bulk of your logic and your rules in the model is a good thing. To help you maintain your model there are some new features that you will want to use:
To help you maintain your model, there are several new features you may want to use:


1. The CheckModel checks all expressions – ViewModel columns and contraints and derived attributes and associations:
'''1.''' The CheckModel checks all expressions – ViewModel columns and constraints and derived attributes and associations:
[[File:05 - VM constraints.png|none|thumb|566x566px]]


kbmvjgj
'''2.''' There is a new CrossReference menu that works on all things in the model - Classes, Attributes, and Associations. You can easily see if an Attribute is in a ViewModel or used by an association, etc:


2. There is a new CrossReference menu that works on all things in the model, Classes,Attributes and Associations. So you can easily see if an Attribute is in a ViewModel or used by an association etc:
[[File:06 - VM constraints.png|none|thumb|568x568px]]


[[Category:MVC]]
[[Category:MVC]]
[[Category:View Model]]
[[Category:View Model]]
{{Edited|July|12|2024}}

Latest revision as of 15:37, 10 February 2024

I made a little MVC sample. I deliberately made it small and simple – but ensured it shows the creation, deletion, and updating of attributes and relations.

01 - VM constraints.png

I have added this sample to the Demos in the general download.

Three ViewModels:

  • One to act as an index page:
02 - VM constraints.png
  • One that shows Example1 and its connected Example2’s:
03 - VM constraints.png
  • And one that shows a single Example2:
04 - VM constraints.png

Then, I made 2 controllers - one for Example1 and one for Example2.

The main goal of using the model-defined ViewModels is to use as little code as possible in the Views and in the Controllers and to also make the code as predictable, simple, and easy to maintain as possible.

The patterns that emerge appear like this:

1. The SHOW STUFF:

        public ActionResult Details(string Identity)
        {
          EcoProject1.Example2 e2 = this.EcoSpace.ExternalIds.ObjectForId(Identity).AsObject as EcoProject1.Example2;
          return View("Details",  VMExample2.Create(this.EcoSpace, e2));
        }

Here we resolve an id to an object, then use the ViewModel as a façade on that object and send it to the view. The benefit is that I do not have to expose my whole business object to the view, and I can also do view specific derivations in the ViewModel.

2. The UPDATE STUFF:

        [HttpPost]
        public ActionResult Details(string Identity, VMExample2 offlinevm)
        {
          Example2 e2 = EcoSpace.ExternalIds.ObjectForId(Identity).AsObject as Example2;
          try
          {
            VMExample2 onlinevm=VMExample2.Create(EcoSpace, e2);
            ViewModelHelper.ApplyValues(offlinevm, onlinevm, null);
            Commit();
            return View("Details", onlinevm);
          }
          catch
          {
            return View("Details", offlinevm);
          }
        }

Here we resolve the id to an object, create an online ViewModel for that object, apply the values from the sent-in offline ViewModel, and commit. The benefits here are mainly that only the things in the ViewModel can change, protecting the rest of your business object. The ViewModel can also be a compilation of details from other parts of your model – and the view need not know anything about this. The ViewModel also has the ability to opt-out of the constraints from the seen classes that do not make sense in this view. This way, class-wide constraints can be used and filtered per view.

3. The DELETE STUFF:

        public ActionResult Delete(string Identity)
        {
          Example1 e1 = this.EcoSpace.ExternalIds.ObjectForId(Identity).AsObject as Example1;
          BusinessDelete(e1);
          Commit();
          return View("Index", VMExample1Index.Create(this.EcoSpace, null));
        }

Again, look up the object from the id, call the BusinessDelete that checks our delete constraints, then prepare a ViewModel for where we should end up afterward. The benefits here are that constraints that are put in place to protect the information from ending up in any illegal state are effectively applied even if the update touches several objects.

4. The CREATE STUFF:

        public ActionResult Create()
        {
          Example1 x1 = new Example1(this.EcoSpace) { Attribute1="HoHoHo",Attribute2="HeHeHe"};
          CommitSkipValidate();
          return View("Index",VMExample1Index.Create(this.EcoSpace, null));
        }

Just create the object and commit, skipping the Validate – since I do not know how to react to objects that break their constraints at birth…

The Views use the ViewModels and get code completion since the ViewModels have generated code:

<table>
@foreach (var item in Model.Example2) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Attribute1)
        </td>
        <td>
            @Html.ActionLink("Details & Edit", "DetailsForExample2", new {  Identity=item.Identity }) |
            @Html.ActionLink("Delete", "DeleteExample2", new { Identity=item.Identity })
        </td>
    </tr>
}
    </table>

Having the bulk of your logic and your rules in the model is a good thing.

Additional Features

To help you maintain your model, there are several new features you may want to use:

1. The CheckModel checks all expressions – ViewModel columns and constraints and derived attributes and associations:

05 - VM constraints.png

2. There is a new CrossReference menu that works on all things in the model - Classes, Attributes, and Associations. You can easily see if an Attribute is in a ViewModel or used by an association, etc:

06 - VM constraints.png
This page was edited 90 days ago on 02/10/2024. What links here