📜 ⬆️ ⬇️

My acquaintance with ASP.NET MVC in Visual Studio 2015 on the example of building a prototype MIS

This year, as a term paper, I had to write a simple medical information system (IIA) for a small private clinic for the treatment of epilepsy.

The patient database in the clinic was already, it was written back in 1998 in Microsoft Access of that time (and even with a beautiful user interface), but now it worked only in one place - on the manager’s computer, and it became completely is impossible. So, there is a need to introduce something new!

No sooner said than done. It was necessary to work quickly (after all, to pass the course time) and at the same time I wanted to make the work as interesting as possible for me. I have long wanted to deal with ASP.NET MVC, I was a little familiar with C # and the general principles of MVC, so I downloaded the latest Visual Studio 2015 RC and got to work.
')
Under the cut - the whole process of acquaintance with technology and the development of such a system with all the found pitfalls. This article is useful to anyone who is familiar with programming and once did a blog on a tutorial for any MVC framework and wants to learn how to do something more interesting.

So, let's begin!

Formulation of the problem


It is necessary to write a single patient database for a small epilepsy clinic. After discussion with the management of the center, the following requirements were formed:

Implementation


The starting point is to understand how ASP.NET MVC works — official tutorials on http://www.asp.net/mvc . For my work for the first time, it was enough of this getting started . I also tried to look at the textbook on Habré , but personally it seemed to me too specialized.

From the manual on the Microsoft website (it’s better to do it by hand in order to feel better), the general mechanics of the process, the work of the controllers and views for one model are excellent, but it’s not clear how to make more complex applications. That's what I'm going to make out.

Having dealt with the basics, we proceed to work.

Create a new ASP.NET application and launch it with Ctrl-F5:

Project creation
image

Model writing


The first stage of our work is a description of the models used. In my case, everything was simple. The need for rapid integration of old data Access dictated the simplest solution - to exactly copy the old data scheme, and only then, if necessary, change it.



In short, that is, the patient (personal data) associated with the techniques (date and doctor). At each appointment, the doctor may add documents of various types: anamnesis, diagnoses, prescriptions, etc. And each such anamnesis, diagnosis and other documents has a type from the dictionary - a separate table with the listing.

Well, create a new Pacient.cs file in the Models folder and describe each model in turn. For example:

Add multiple models
public class DiagnosisType { public int ID { get; set; } [DisplayName("")] public String name { get; set; } [DisplayName("")] [DataType(DataType.MultilineText)] public String description { get; set; } } public class Diagnosis { public int ID { get; set; } [DisplayName("")] public DiagnosisType type { get; set; } [DisplayName("")] [DataType(DataType.MultilineText)] public String comments { get; set; } } public class VisitDate { public int ID { get; set; } public int doctorID { get; set; } [DisplayName(" ")] public DateTime date { get; set; } public List<Anamnesis> anamnesis { get; set; } public List<Debut> debutes { get; set; } public List<Diagnosis> diagnoses { get; set; } public List<Research> researches { get; set; } public List<Assigment> assigments { get; set; } public List<Neurostatus> neurostatuses { get; set; } public List<Review> reviews { get; set; } public List<Syndrome> syndromes { get; set; } } public enum Sex { [Display(Name = "")] A, [Display(Name = "")] F, [Display(Name = "")] M, [Display(Name = " ")] N, [Display(Name = "")] O, [Display(Name = "")] U } public class Pacient { public int ID { get; set; } [DisplayName(" ")] public Doctor doctor { get; set; } [DisplayName("")] public String name { get; set; } [DisplayName(" ")] public String cart { get; set; } [DisplayName("")] [DataType(DataType.PhoneNumber)] [Phone] public String phone { get; set; } [DisplayName("   ")] [DataType(DataType.Date)] public DateTime dateOfregistration { get; set; } [DisplayName("")] public Sex sex { get; set; } [DisplayName(" ")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] public DateTime birthday { get; set; } [DisplayName("")] public String mother { get; set; } [DisplayName("")] public String father { get; set; } [DisplayName(" ")] public String adress { get; set; } [DisplayName("")] [DataType(DataType.Html)] [AllowHtml] public String comments { get; set; } public List<VisitDate> visits { get; set; } } 


The remaining classes from the model are determined by analogy.

Remarks:


General ideas for generating database tables from models in the Entity Framework:

Great, we designed all the models. Now you need to tell the Entity Framework that these are models for the database. To do this, create a new connection context. One context - one database .

Context creation code
 public class PacientDBContext : DbContext { public DbSet<Pacient> pacients { get; set; } public DbSet<AnamnesisEventType> anamnesisTypes { get; set; } public DbSet<Anamnesis> anamneses { get; set; } public DbSet<Debut> debutes { get; set; } public DbSet<DebutType> debuteTypes { get; set; } public DbSet<Diagnosis> diagnoses { get; set; } public DbSet<DiagnosisType> diagnosisTypes { get; set; } public DbSet<Research> researches { get; set; } public DbSet<ResearchType> researchTypes { get; set; } public DbSet<Medicine> medicines { get; set; } public DbSet<MedicineType> medicineTypes { get; set; } public DbSet<Neurostatus> neurostatuses { get; set; } public DbSet<NeuroStatusType> neuroStatusTypes { get; set; } public DbSet<Assigment> assigments { get; set; } public DbSet<AssigmentType> assigmentTypes { get; set; } public DbSet<Syndrome> syndromes { get; set; } public DbSet<SyndromeType> syndromeTypes { get; set; } public DbSet<Review> reviews { get; set; } public DbSet<VisitDate> visits { get; set; } public DbSet<Doctor> doctors { get; set; } } </lang> </spoiler> , , ,    .    -      Web.config   : <spoiler title="  Web.config"> <source lang="xml"> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Users.mdf;Initial Catalog=aspnet-WebApplication2-20150526031246;Integrated Security=True" providerName="System.Data.SqlClient" /> <add name="PacientDBContext" connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Pacients.mdf;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> 


Attention! Substitute! By default, Data Source = (LocalDb) \ MSLocalDB is written in the Default Context, it turned out that it was SQL Server Express 2014 before deployment, but my hosting did not know anything at all about it! It is better to immediately put Express 2012 (if you don’t have it) and fix it on v11.0 .

Now it remains only to launch the application and the system will create a new database ... Or not? For me this happened only at the first request for access to this data. But after accessing the data on the left in Server Browser, you can observe the database created for us:



By the way, if afterwards the model should be slightly changed or something added, there is no need to recreate the base. For this there are automatic migrations. Order of work: open the package manager console, enable migrations with the command Enable-Migrations –EnableAutomaticMigrations -ContextTypeName WebApplication2.Models.PacientDBContex , to update the database in the future we give the update-database command. Read more - here .



Adding Controllers


The next step is to add controllers for each data type in the system. The process is automatic-manual. You need to add controllers for each data type.

The process of adding a controller




Great, now we have controllers and standard views for viewing, adding and changing all the data! Probably they can be linked, as in the admin Django ... or not?

And no! In the standard, there is no way to associate the patient with the date of admission, although there is such a connection at the model level. From this moment begins the most interesting! We need to display the patient page with all its data.

To do this in PacientsContrtoller.cs change the Details method:

 public ActionResult Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } //db. Pacient pacient = db.pacients .Include(p=>p.doctor) .Include(p => p.visits.Select(w => w.anamnesis.Select(r=>r.type))) .Include(p => p.visits.Select(w => w.debutes.Select(r => r.type))) .Include(p => p.visits.Select(w => w.diagnoses.Select(r => r.type))) .Include(p => p.visits.Select(w => w.researches.Select(r => r.type))) .Include(p => p.visits.Select(w => w.anamnesis.Select(r => r.type))) .Include(p => p.visits.Select(w => w.neurostatuses.Select(r => r.type))) .Include(p => p.visits.Select(w => w.assigments.Select(r => r.type))) .Include(p => p.visits.Select(w => w.syndromes.Select(r => r.type))) .Include(p => p.visits.Select(w => w.reviews)) .Where(p=>p.ID == id).Single(); pacient.visits.Sort(delegate (VisitDate t1, VisitDate t2) { return t2.date.CompareTo(t1.date); }); return View(pacient); } 

In this terribly ugly LINQ request, we ask the system to load absolutely all patient data. To do this, use Include, and for the second level of nesting - Select.

To implement a search by name or by word in the admission summary, we also use a clever query:

 public ActionResult SearchByName(String name = "", String mode = "name") { if (mode.Equals("name")) return PartialView(db.pacients.Where(p => p.name.Contains(name)).ToList()); else { var results = db.pacients.Where(p => p.visits.Any(vd => vd.reviews.Any(r => r.comments.ToLower().Contains(name.ToLower())))); return PartialView(results.ToList()); } } 

I remind you that the parameters for the controller method is what comes in the GET request (in the address bar after the question mark).

The remaining methods are essentially unchanged.

In each of the controllers for documents (anamnesis, diagnoses, summaries ...) I created 4 new methods in the replacement of standard ones, they will return partial (partial) representations on AJAX:

Implementation
 public ActionResult pacientDetails(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Anamnesis anamnesis = db.anamneses.Include(p => p.type).Where(p => p.ID == id).First(); if (anamnesis == null) { return HttpNotFound(); } return PartialView("~/views/Anamnesis/pacientDetails.cshtml", anamnesis); } // GET: Anamnesis/Create public ActionResult pacientCreate(int visitID, int num) { newAnamnesis na = new newAnamnesis(); na.visitID = visitID; na.num = num; na.anamnesis = new Anamnesis(); na.eventTypes = db.anamnesisTypes.ToList(); return PartialView(na); } public ActionResult Create(newAnamnesis data) { VisitDate visit = db.visits.Include(v => v.anamnesis).Where(v => v.ID == data.visitID).First(); if (visit == null) return RedirectToAction("Index", "Pacients"); Pacient pacient = db.pacients.Where(p => p.visits.Any(v => v.ID == data.visitID)).First(); if (pacient == null) return RedirectToAction("Index", "Pacients"); if (ModelState.IsValid) { AnamnesisEventType type = db.anamnesisTypes.Where(a => a.ID == data.anamnesis.type.ID).First(); data.anamnesis.type = type; visit.anamnesis.Add(data.anamnesis); db.SaveChanges(); return PartialView("/views/Anamnesis/pacientDetails.cshtml", data.anamnesis); } return PartialView("/views/Anamnesis/pacientCreate.cshtml", data); } // GET: Anamnesis/Edit/5 public ActionResult pacientEdit(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Anamnesis anamnesis = db.anamneses.Include(p=>p.type).Where(p=>p.ID == id).First(); if (anamnesis == null) { return HttpNotFound(); } return PartialView(anamnesis); } // POST: Anamnesis/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult pacientEdit([Bind(Include = "ID,comments")] Anamnesis anamnesis) { if (ModelState.IsValid) { db.Entry(anamnesis).State = EntityState.Modified; db.SaveChanges(); return pacientDetails(anamnesis.ID); } return PartialView(anamnesis); } // GET: Anamnesis/Delete/5 public ActionResult pacientDelete(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Anamnesis anamnesis = db.anamneses.Find(id); if (anamnesis == null) { return HttpNotFound(); } db.anamneses.Remove(anamnesis); db.SaveChanges(); return PartialView(); } 


Modify standard views


Now our task is to change the presentation for the patient so that they display all the information about him and allow him to edit without updating the page.

On the main page, we will show the search form in two options to choose from and using JQuery to dynamically load the results.

Patient List Page Code - Views / Pacient / Details.cshtml
 @{ ViewBag.Title = "Index"; } <div class="row"> <div class="col-md-6"> <h2> </h2> </div> <div class="col-md-6"> <a href="/Pacients/Create/" class="btn btn-success pull-right" style="margin-top: 20px; margin-right: 20px;"><span class="glyphicon glyphicon-plus"></span></a> </div> </div> <div> <form class="form-horizontal"> <div class="input-group input-group-lg col-md-12 bs-callout bs-callout-primary"> <label for="search" class="sr-only">  </label> <div class="col-sm-10"> <input type="text" placeholder="  " name="name" class="col-sm-10 form-control" id="search" /> </div> <div class="col-sm-2"> <input type='button' id="submit" value='' class="btn" /> </div> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="searchOptions" id="searchByName" value="name" checked>   </label> <label class="radio-inline"> <input type="radio" name="searchOptions" id="reviewSearch" value="review">   </label> </div> </div> </form> </div> <div id="results"></div> <script type="text/javascript"> $(document).ready(function () { //$('#submit').cha $('#submit').click(function (e) { e.preventDefault(); var name = $('#search').val(); var mode = "name"; if ($("#reviewSearch").prop("checked")) { mode = "review" } name = name.replace(new RegExp(" ", 'g'), "%20"); $('#results').load("/Pacients/SearchByName?name=" + name + "&mode="+mode); }); $('#search').keypress(function (event) { if ($("#reviewSearch").prop("checked")) return; if (event.which == 13) { event.preventDefault(); } var name = $('#search').val(); var mode = "name"; if ($("#reviewSearch").prop("checked")) { mode = "review" } name = name.replace(new RegExp(" ", 'g'), "%20"); $('#results').load("/Pacients/SearchByName?name=" + name + "&mode=" + mode); }); }); </script> 


Lets create new patient patterns and changes without changes. But the patient information page had to be split into several submissions at once.

Patient page header
 @model WebApplication2.Models.Pacient @{ ViewBag.Title = "Details"; } <div class="row"> <div class="col-md-2" style=" margin-top: 30px;"> <a href="@Url.Action("Index" )" class = "btn btn-default btn-lg"><span class="glyphicon glyphicon-backward" aria-hidden="true"></span>  </a> </div> <div class="col-md-6"><h2>@Html.DisplayFor(model => model.name)</h2> <h4>@Html.DisplayNameFor(model => model.doctor): @Html.DisplayFor(model => model.doctor.name)</h4> </div> @if (Model.visits.Count==0 || !(Model.visits.First().date.Equals(DateTime.Today))) { <span style=" margin-top: 30px;margin-right: 30px;" class="pull-right"> <a href="@Url.Action("Create", "visitDates", new {id=Model.ID })" class="btn btn-default btn-primary btn-lg"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>  </a> </span> } </div> <hr /> @Html.Partial("~/Views/Pacients/visitsView.cshtml", Model) 


The cap command Html. Partial ("~ / Views / Pacients / visitsView.cshtml", Model) loads the list of all documents. At the top of the list of documents are general JS functions for the work of dynamic data loading. VS2015 supports AngularJS by default, but in this project I decided to do without it - easier, but more understandable. Fortunately, we needed only four procedures.

The receptions tab contains all data sorted by reception date, the rest of the tab contains individual types of documents. To simplify, everything is again spaced out in different ways.

Views / Pacients / visitsView.cshtml
 @model WebApplication2.Models.Pacient @using WebApplication2.Models <script> function Delete(controller, id) { if (confirm("     ?")) { $('#' + controller + 'Div' + id).load('/' + controller + '/pacientDelete/' + id); $('.' + controller + 'Div' + id).load('/' + controller + '/pacientDelete/' + id); } } function Cancel(controller, id) { if (confirm("  ,      . ?")) { $("#" + controller + "Tab").find('#' + controller + 'Div' + id).load('/' + controller + '/pacientCancel/' + id); $("#" + controller + "Tab").find('.' + controller + 'Div' + id).load('/' + controller + '/pacientCancel/' + id); } } function CancelEdit(controller, id) { $.get('/' + controller + '/pacientDetails/' + id, function (data) { res = $.parseHTML('<div>' + data + '</div>'); if ($(res).find('.' + controller + 'Div' + id).html() != "") { var content = $(res).find('.' + controller + 'Div' + id).html(); } else { var content = $(res).find('#' + controller + 'Div' + id).html(); } $('#' + controller + 'Div' + id).html(content); $('.' + controller + 'Div' + id).html(content); }); } function LoadEditForm(controller, id) { if ($("#" + controller + "Tab").hasClass("active")) { $("#" + controller + "Tab").find('#' + controller + 'Div' + id).load('/' + controller + '/pacientEdit/' + id); $("#" + controller + "Tab").find('.' + controller + 'Div' + id).load('/' + controller + '/pacientEdit/' + id); } else { $("#dateTab").find('#' + controller + 'Div' + id).load('/' + controller + '/pacientEdit/' + id); $("#dateTab").find('.' + controller + 'Div' + id).load('/' + controller + '/pacientEdit/' + id); } } function PostEditForm(controller, id, mce) { if (mce == true) tinyMCE.triggerSave(); $.ajax({ type: "POST", url: '/' + controller + '/pacientEdit/' + id, data: $('.' + controller + 'Edit' + id).serialize() + $('#' + controller + 'Edit' + id).serialize(), // serializes the form's elements. success: function (data) { res = $.parseHTML('<div>' + data + '</div>'); if ($(res).find('.' + controller + 'Div' + id).html() != "") { var content = $(res).find('.' + controller + 'Div' + id).html(); } else { var content = $(res).find('#' + controller + 'Div' + id).html(); } $('#' + controller + 'Div' + id).html(content); $('.' + controller + 'Div' + id).html(content); } }); } function PostCreateForm(controller, num, mce) { if (mce == true) tinyMCE.triggerSave(); $.ajax({ type: "POST", url: '/' + controller + '/Create/', data: $('#' + controller + 'Create').serialize(), // serializes the form's elements. success: function (data) { res = $.parseHTML('<div><div>' + data + '</div></div>'); $('#documentData' + num).prepend($(res).find('div').first().html()); $('#' + controller + 'Tab').find('.tabContent').prepend($(res).find('div').first().html()); $('#' + controller + 'Create').trigger('reset'); } }); } </script> <div role="tabpanel"> <ul class="nav nav-pills nav-justified"> <li role="presentation" class="active"><a href="#dateTab" aria-controls="dateTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#InfoTab" aria-controls="InfoTab" role="tab" data-toggle="tab"> </a></li> <li role="presentation"><a href="#AnamnesisTab" aria-controls="AnamnesisTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#DebutsTab" aria-controls="DebutsTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#DiagnosesTab" aria-controls="DiagnosesTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#SyndromesTab" aria-controls="SyndromesTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#ResearchesTab" aria-controls="ResearchesTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#AssigmentsTab" aria-controls="AssigmentsTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#NeurostatusTab" aria-controls="NeurostatusTab" role="tab" data-toggle="tab"></a></li> <li role="presentation"><a href="#ReviewsTab" aria-controls="ReviewsTab" role="tab" data-toggle="tab"></a></li> </ul> <div class="tab-content"> <div role="tabpanel" class="tab-pane active fade in" id="dateTab"> @if (Model.visits.Count == 0) { <div class="bs-callout bs-callout-success"> <p>     .</p> </div> } @if (Model.visits.Count != 0 && Model.visits.First().date.Equals(DateTime.Today)) { @Html.Partial("~/Views/Pacients/documentList.cshtml", new documentList { num = 1, add = true, visit = Model.visits.First() }) } @{ int num = 9; } @foreach (var visit in Model.visits) { if (visit.date.Equals(DateTime.Today)) { continue; } @Html.Partial("~/Views/Pacients/documentList.cshtml", new documentList { num = num, add = false, visit = visit }) num = num + 8; } </div> <div role="tabpanel" class="tab-pane fade" id="InfoTab"> @Html.Partial("~/Views/Pacients/PersonalData.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="AnamnesisTab"> @Html.Partial("~/Views/Pacients/anamnesisList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="DebutsTab"> @Html.Partial("~/Views/Pacients/debutList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="DiagnosesTab"> @Html.Partial("~/Views/Pacients/diagnosisList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="SyndromesTab"> @Html.Partial("~/Views/Pacients/syndromList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="ResearchesTab"> @Html.Partial("~/Views/Pacients/researchList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="AssigmentsTab"> @Html.Partial("~/Views/Pacients/assigmentList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="NeurostatusTab"> @Html.Partial("~/Views/Pacients/neurostatusList.cshtml", Model) </div> <div role="tabpanel" class="tab-pane fade" id="ReviewsTab"> @Html.Partial("~/Views/Pacients/reviewList.cshtml", Model) </div> </div> </div> 



For example, consider one of the views for the tab, for example, with anamnesis.

Views / Pacient / anamnesisList.cshtml
 @model WebApplication2.Models.Pacient @using WebApplication2.Models <div class="bs-callout bs-callout-success"> <h5> </h5> <p> </p> <div class="tabContent"> @foreach (var visit in Model.visits) { foreach (var anamnes in visit.anamnesis) { @Html.Partial("~/Views/Anamnesis/pacientDetails.cshtml", anamnes) } } </div> </div> 



As you remember, we made four new methods in document controllers: pacientDetails, pacientCreate, pacientEdit, pacientDelete. It is necessary of them to refer the presentation above. So you need to create it!

Adding a new partial view



Having created a presentation, we fill it in:

Views / Anamnesis / pacientDetails.cshtml
 @model WebApplication2.Models.Anamnesis <div class="@String.Format("AnamnesisDiv{0}", Model.ID) row"> <div class="col-md-4"><strong> @Html.DisplayFor(model => model.type.name) </strong> </div> <div class="col-md-6"><p> @Html.DisplayFor(model => model.comments) </p> </div> <div class="col-md-2"> <button class="btn btn-success btn-sm" onclick="LoadEditForm('Anamnesis', @Model.ID)"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> </button> <button class="btn btn-danger btn-sm" onclick="Delete('Anamnesis', @Model.ID)"> <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> </button> </div> </div> <hr /> 


Html .DisplayFor (model => model.type.name) is used instead of the usual names to display the element name. This allows you to set names inside the models (as was done at the beginning of the post).

Similarly, you can do with the presentation to change the history:

Views / Anamnesis / pacientEdit.cshtml
 @model WebApplication2.Models.Anamnesis <form class="@String.Format("AnamnesisEdit{0}", Model.ID)"> @Html.AntiForgeryToken() @Html.HiddenFor(model => model.ID) @Html.HiddenFor(model => model.type.ID) <div class="form-horizontal"> <div class="col-md-4"> <strong> @Html.DisplayFor(model => model.type.name) </strong> </div> <div class="col-md-6"> @Html.EditorFor(model => model.comments, new { htmlAttributes = new { @class = "form-control", @placeholder = Html.DisplayNameFor(model => model.comments) } }) @Html.ValidationMessageFor(model => model.comments, "", new { @class = "text-danger" }) </div> <div class="col-md-2"> <a onclick="CancelEdit('Anamnesis', @Model.ID);" class="btn btn-warning btn-sm"><span class="glyphicon glyphicon-backward" aria-hidden="true"></span></a> <a onclick="PostEditForm('Anamnesis', @Model.ID);" class="btn btn-primary btn-sm"><span class="glyphicon glyphicon-save" aria-hidden="true"></span></a> </div> </div> </form> 


Remarks:


But with the creation of new objects more difficult: we need to associate it with an object from the dictionary, and this is another model and another table with data. It is necessary to drag a list of all possible options into the creation form. It’s easy to load them, but you cannot transfer to the view - it takes only one parameter - the model. We'll have to create a new model ...

We create a new Models \ viewModels.cs file and add our model code for presentation there. It is not necessary to add it to the context.

Model code for adding anamnesis
 public class newAnamnesis { public Anamnesis anamnesis { get; set; } public int visitID { get; set; } public int? num { get; set; } public List<AnamnesisEventType> eventTypes { get; set; } } 



Now, recall our pacientCreate function in Controllers \ AnamnesisController.cs:

pacientCreate - method of adding a new history
 // GET: Anamnesis/Create public ActionResult pacientCreate(int visitID, int num) { newAnamnesis na = new newAnamnesis(); na.visitID = visitID; na.num = num; na.anamnesis = new Anamnesis(); na.eventTypes = db.anamnesisTypes.ToList(); return PartialView(na); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(newAnamnesis data) { VisitDate visit = db.visits.Include(v => v.anamnesis).Where(v => v.ID == data.visitID).First(); if (visit == null) return RedirectToAction("Index", "Pacients"); Pacient pacient = db.pacients.Where(p => p.visits.Any(v => v.ID == data.visitID)).First(); if (pacient == null) return RedirectToAction("Index", "Pacients"); if (ModelState.IsValid) { AnamnesisEventType type = db.anamnesisTypes.Where(a => a.ID == data.anamnesis.type.ID).First(); data.anamnesis.type = type; visit.anamnesis.Add(data.anamnesis); db.SaveChanges(); return PartialView("/views/Anamnesis/pacientDetails.cshtml", data.anamnesis); } return PartialView("/views/Anamnesis/pacientCreate.cshtml", data); } 


Fuh, it seems now everything is ready. It remains only to do these operations for all other types of documents. By the way, in theory, this can be automated, say, to create a template representation, but you cannot do this in Razor. Correct if I'm wrong.

The creation form is loaded on the tricks tab:

View code documentList.cshtml for the Receptions tab
 @model WebApplication2.Models.documentList @using WebApplication2.Models @{ var cl = "bs-callout-primary"; var ac = ""; } @if (Model.add == false) { cl = "bs-callout-success"; ac = ""; } <div class="bs-callout @cl"> @if (Model.add == false) { <h5> @Model.visit.date</h5> } else { <h5> </h5> } @if (Model.visit.anamnesis.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var anamnes in Model.visit.anamnesis) { @Html.Partial("~/Views/Anamnesis/pacientDetails.cshtml", anamnes); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Anamnesis", new { num = Model.num, visitID = Model.visit.ID}) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.debutes.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var debut in Model.visit.debutes) { @Html.Partial("~/Views/Debuts/pacientDetails.cshtml", debut); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Debuts", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.diagnoses.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var diagnosis in Model.visit.diagnoses) { @Html.Partial("~/Views/Diagnoses/pacientDetails.cshtml", diagnosis); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Diagnoses", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.syndromes.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var syndrome in Model.visit.syndromes) { @Html.Partial("~/Views/Syndromes/pacientDetails.cshtml", syndrome); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Syndromes", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.researches.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var research in Model.visit.researches) { @Html.Partial("~/Views/Researches/pacientDetails.cshtml", research); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Researches", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.assigments.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var assigment in Model.visit.assigments) { @Html.Partial("~/Views/Assigments/pacientDetails.cshtml", assigment); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Assigments", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.neurostatuses.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var neurostatus in Model.visit.neurostatuses) { @Html.Partial("~/Views/Neurostatus/pacientDetails.cshtml", neurostatus); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Neurostatus", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } @if (Model.visit.reviews.Count > 0 || Model.add == true) { <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="@String.Format("#documentHeading{0}", Model.num)" onclick="$('@String.Format("#document{0}", Model.num)').collapse('toogle');"> <h4 class="panel-title"> <a data-toggle="collapse" data-parent="#accordion" href="@String.Format("#document{0}", Model.num)" aria-expanded="true" aria-controls="collapseOne"> <span class="pull-right"><small>@Model.visit.date</small></span> </a> </h4> </div> <div id="@String.Format("document{0}", Model.num)" class="panel-collapse collapse @ac" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <div id="@String.Format("documentData{0}", Model.num)"> @foreach (var review in Model.visit.reviews) { @Html.Partial("~/Views/Reviews/pacientDetails.cshtml", review); } </div> @if (Model.add == true) { @Html.Action("pacientCreate", "Reviews", new { num = Model.num, visitID = Model.visit.ID }) } </div> </div> </div> </div> Model.num = Model.num + 1; } <div class="row"> @Html.ActionLink("     ", "Delete", "VisitDates", new { id = Model.visit.ID }, new { @class = "btn btn-danger btn-sm pull-right", style = "margin-top: 10px;margin-right: 15px;" }) </div> </div> 


For him, by the way, it also required a separate presentation.

The result was this:





Publish to server


To deploy to your server via FTP, you need to deploy the project and separately deploy the database. To do this, right-click on the project and select the publication.

Before downloading, you need to edit the Web.Release.Config in the project root, writing connectionString into it to connect to the database on the server. Instructions on how to do this are carefully provided by Microsoft right in the file itself.

Exporting the database had to be done manually through SQL Server Management Studio.

Everything should be easier with Azure - the studio will publish the database itself.

Conclusion


Unfortunately, it is extremely difficult to describe all the subtleties in one article, but I tried to highlight the general points. The full project code is available on GitHub: https://github.com/roctbb/ICNE_EHR/ .

I guess that I did some things not quite right, I will be glad to any comments.

The sources used asp.net site and countless questions on stackoverflow.com.

Source: https://habr.com/ru/post/260867/


All Articles