ASP.NET MVC - updating foreign keys - asp.net-mvc

I am just starting to investigate ASP.NET MVC, and I am using the latest beta (ie. 4). I'm after the correct approach to what is a fairly CRUD scenario. My main table (Task) looks something like this -
TaskID (int)
EmployeeID (int)
ProjectID (int)
DeptID (int)
Comment (varchar)
Date (datetime)
Hours (float)
TaskID is the primary key. The other three IDs are all foreign keys into reference tables.
Following various tutorials, I created an object model (.edmx) using Entity Framework. I then autogenerated the controller using "Add... Controller" and selecting the "Controller with read/write..." template.
All worked well. However, obviously I want the three foreign key columns to display lookup values from the reference tables, rather than the ID. I'm really not sure what the "best practice" method for achieving this is. A few options occur to me -
Create a view in SQL Server
Create a view in EF (not sure how this is done)
Look up the reference values on the fly using LINQ in the controllers
Perhaps there are other ways. I would like to hear from experienced MVC progs regarding "best practice" in this scenario.

would prefer to have TaskViewModel class which will have properties something like this
public class TaskViewModel
{
public Task Task { get; set; }
public Dictionary<int, string> ProjectList { get; set; }
//rest of the Lookup Properties like DeptList, EmpList
}
public class Task
{
public int TaskId { get; set; }
public int projectId { get; set; }
//Rest of the properties
}
And would use
#model TaskViewModel
#Html.DropDownListFor(m => m.Task.projectId, new SelectList(Model.ProjectList, "key", "value", Model.Task.projectId))%>

Related

Setting up for selective auditing in same DbContext

I just stumbled onto Audit.Net and I'm hooked. I went through the Audit.Net Entity Framework (6) documentation and am a bit lost in the output part.
My solution is a bit many-layers design:
Mvc 5 website
Wcf Client
Using WcfClientProxyGenerator
Wcf Service
Separate IService Contracts Library
All interfaces expose BDOs
Business Logic
Seperate BDO Library
Data Access
Reference EF6
Reference Audit.Net
DTOs
Entity Framwework Library
Has EDMX only
Reference Audit.Net
Reference: My EDMX is named Focus
Usage
I manage to modify the FocusModel.Context.tt from:
partial class <#=code.Escape(container)#> : DbContext
To:
partial class <#=code.Escape(container)#> : Audit.EntityFramework.AuditDbContext
Configuration
I found the default setting for Mode, IncludeEntityObjects, & AuditEventType were to my liking. the attribute for Include/Ignore entities/properties were straightforward as well.
Output
This is where I'm confused. I need to audit to the same database preferably to Audit tables for selected entities. Every entity in my database has composite PKs. How do I set the output mode in this scenario? Also, in my solution setup, the starting point for all projects that are behind the WCF Service is the WCF service itself. Does this mean that the point to Fluent-API-configure Audit.Net is here?
Have you seen the main Audit.NET documentation, specifically the output data providers?
I need to audit to the same database preferably to Audit tables for selected entities. Every entity in my database has composite PKs.
So you can use the EF data provider. It works with any kind of primary key.
How do I set the output mode in this scenario?
I'm not sure what do you mean by output mode, but I'm guessing you ask about OptIn/OptOut to ignore your audit entities to be audited. If that's the case you have multiple options, like using AuditIgnore attribute on your audit POCO classes, or via the fluent-api OptIn()/OptOut() methods. See example below.
The starting point for all projects that are behind the WCF Service is the WCF service itself. Does this mean that the point to Fluent-API-configure Audit.Net is here?
You can configure the Audit.NET library in any place, but you must do it before any audit event creation, so it is recommended to be on your startup code, as soon as your app or service starts.
Sample code
The following is a minimal example showing how you can configure the Audit.NET and Audit.EntityFramework libraries.
Suppose you have the following schema:
public class Student
{
public int PK_1 { get; set; }
public string PK_2 { get; set; }
public string Name { get; set; }
}
public class Student_Audit
{
public int PK_1 { get; set; }
public string PK_2 { get; set; }
public string Name { get; set; }
public DateTime AuditDate { get; set; }
public string AuditAction { get; set; }
}
public class SchoolContext : AuditDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder) //<--Tip: its not DbModelBuilder, its Microsoft.EntityFrameworkCore.ModelBuilder
{
modelBuilder.Entity<Student>().HasKey(c => new { c.PK_1, c.PK_2 });
modelBuilder.Entity<Student_Audit>().HasKey(c => new { c.PK_1, c.PK_2, c.AuditDate });
}
public DbSet<Student> Students { get; set; }
public DbSet<Student_Audit> Students_Audit { get; set; }
}
You can configure the library on your startup code as follows:
// Setup audit to use the EF data provider
Audit.Core.Configuration.Setup()
.UseEntityFramework(_ => _
.AuditTypeExplicitMapper(m => m
// Map Student to Student_Audit
.Map<Student, Student_Audit>((ev, ent, studentAudit) =>
{
//add the action name and the date to the audit entity
studentAudit.AuditAction = ent.Action;
studentAudit.AuditDate = DateTime.UtcNow;
})));
// Configure the EF audit behavior
Audit.EntityFramework.Configuration.Setup()
.ForContext<SchoolContext>(_ => _.IncludeEntityObjects())
.UseOptOut()
.Ignore<Student_Audit>(); // Do not audit the audit tables
And a test case:
using (var db = new SchoolContext())
{
db.Database.EnsureCreated();
var st = new Student() { PK_1 = 1, PK_2 = "one", Name = "John" };
db.Students.Add(st);
db.SaveChanges();
}
Will generate the following:

Using the method in two model in one view of asp.net mvc 2.0

I have one view in my ASP.net MVC 2.0 project, I want to list the list of employee that I create method GetProfileCustomer() in CustomerModels and GetTransaction() in TransactionModels.
How can I import two different of models in a single view?
I'm fairly new to MVC as well, and I've struggled with the similar issues if I understand the question correctly.
I've found that you get much cleaner controller code if you design your ViewModels to be as close as possible to the data that the view is using. Your ViewModels can contain lists of other things including other model objects. Something like:
public class TransactionViewModel
{
public string dataelement1 { get; set; }
public int dataelement2 { get; set; }
//and so on...
//The Lists
public IList<Employee> EmpList { get; set; }
public IList<OtherModel> SomethingElse { get; set; }
//and so on...
}
In your controller, you construct and initialize your ViewModel
something like...
TransactionViewModel TVM = new TransactionViewModel();
//assign basic attributes here..
//make a list
TVM.Emplist = (from blah in context select blah).ToList();
//send it to the view
return View(TVM);
Hope this helps and happy to hear any feedback...

How should AutoMapper access my DAL?

I have an InvoiceInputModel with a ProjectId property which is a reference to a Project entity. Ideally, I want AutoMapper to be able to map an entire Invoice entity from an InvoiceInputModel, which looks like this:
public class InvoiceInputModel
{
public Guid Id { get; set; }
public DateTime Date { get; set; }
public string Reference { get; set; }
public Guid ProjectId { get; set; }
}
Obviously the following is bad:
Mapper.CreateMap<InvoiceInputModel, Invoice>()
.ForMember(src => src.Project, opt => opt.MapFrom(
dest => _unitOfWork.CurrentSession.Get<Project>(dest.ProjectId)
)
);
How do I tell AutoMapper that invoice.Project should be mapped to a Project entity based off of the ProjectId property in InvoiceInputModel while preserving loose coupling?
Invoice/Edit in my InvoiceController:
[HttpPost]
[Authorize]
public ActionResult Edit(InvoiceInputModel invoiceInputModel)
{
var invoice = _unitOfWork.CurrentSession.Get<Invoice>(invoiceInputModel.Id);
Mapper.Map<InvoiceInputModel, Invoice>(invoiceInputModel, invoice);
invoice.Project = _unitOfWork.CurrentSession.Get<Project>(invoiceInputModel.ProjectId);
// I want AutoMapper to do the above.
_unitOfWork.CurrentSession.SaveOrUpdate(invoice);
_unitOfWork.Commit();
return View(invoice);
}
I spotted something about "Resolvers" and ResolveUsing, but I have no experience using it.
How do I tell AutoMapper to do this while preserving loose coupling between my entity models, input models and view models? Or is there a better way?
How do I tell AutoMapper that invoice.Project should be mapped to a Project entity based off of the ProjectId property in InvoiceInputModel while preserving loose coupling?
You can't. If AutoMapper is going somewhere else to fetch data, then it's not loose coupled.
You're not modifying the Project in this particular View anyway - why do you need to set the relationship to Project, isn't nHibernate smart enough to see that property hasn't changed, and not do anything?
I personally have entities in viewmodels instead of IDs, so that binding happens automatically.
http://sprokhorenko.blogspot.com/2011/03/bind-to-entity-id.html

ASP.NET MVC - Partially updating model from view

I just wondered how people were approaching this situation. It's something that seems like a weak point in my usage of MVC with ORMs (NHibernate in this case)...
Say you have a fine-grained and complicated entity in your model. You will likely have an admin page to manage objects of this type. If the entity is complicated, it is unlikely that you will be modifying the whole entity in one form. You still need to pass the relevant properties to the view, and incorporate changes to those properties in the model when the view returns them.
What does anyone do in this situation?
Create a view model which is (or contains) a subset of the entities properties. Pass this to and from the view. In 'edit' action method in controller, get the object from repository, go though all the properies in the ViewModel and apply them to the Model object (model.a = viewmodel.a, modelb = viewmodel.b). This seems the obvious sensible route, but generates a lot of tedious plumbing code. Also this complicates validation a bit.
Something else?
I've looked briefly at automapper - but this doesn't seem to fit the bill exactly, maybe I'm wrong?
Thanks.
This sounds like the perfect scenario for automapper. You create a view model class which contains a subset of the fields or your real model, and you let AutoMapper take care extraccting values from the domain model object into your view model object. What issues are you having with this approach?
Consider this example:
Here is your domain model and your view model
public class Person
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public string HomeNumber
{ get; set; }
public string Address1
{ get; set; }
public string Address2
{ get; set; }
}
public class PersonViewModel
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public string HomeNumber
{ get; set; }
}
Here is your mapping, you have to create a mapping in both directions from dm->vm and vm->dm.
From what I've seen when using Automapper is that if you map from object A to B and B has a property which A doesn't have, it will be reset. So when I create the map I direct it to ignore those missing properties. I'm not a Automapper expert so I may be using it wrong.
Mapping
Mapper.CreateMap<Person, PersonViewModel>();
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them!
Mapper.CreateMap<PersonViewModel, Person>()
.ForMember(dest => dest.HomeNumber, opt => opt.Ignore());
Finally usage:
Person p = new Person()
{
FirstName = "First",
LastName = "Last",
Address1 = "add 1",
Address2 = "add 2"
};
PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p);
// Map to a new person
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm);
// Map to the existing person just to update it
Person p3 = new Person()
{
HomeNumber = "numberHere"
};
// This will update p3
Mapper.Map<PersonViewModel, Person>(pvm, p3);
Because of the exclusion, this is obviously less than ideal, but much better than manually doing the whole thing.
Have your view model map one-to-one with your domain model.
Specify Model as argument for the routeValues as below. This means your view model will be initialized with the values from the domain model. Only the sub set of fields in the form will be overwritten in the resulting personViewData.
Update View:
#model ViewModel.PersonView
#using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post))
{
...Put your sub set of the PersonView fields here
}
ProfileController:
public ActionResult Update(string userName)
{
Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault();
PersonView personView = new PersonView();
Mapper.Map(person, personView);
return View(personView);
}
[HttpPost]
public ActionResult Update(PersonView personViewData)
{
Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault();
Mapper.Map(personViewData, person);
_unitOfWork.Person.Update(person);
_unitOfWork.Save();
return Json(new { saved = true, status = "" });
}
Why don't you use TryUpdateModel with the form collection.
If your view is editing a person
public class Person
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Address { get; set; }
}
And your view is only editing first name and last name, you can do this:
public ActionResult Action(FormCollection form)
{
Person personToUpdate = Repository.GetPerson(form["ID"]);
TryUpdateModel<Person>(personToUpdate, form);
Repository.Update(personToUpdate)
return View();
}
That will only update Person with the items that a part of the form collection. If you don't want a field updated, don't submit it with the form.
What if you have full model but each page uses and updates only the required part? Then you update the business model using complete view data at the last page.
I use a similar approach to yours (in my case Entity Framework) with Entity -> ViewModel -> View but only on views with "complex" entities that have either 1:M or M:M relationships. In most cases I took the low road and went for Entity->View when I have a simple entity.
My ViewModel is defined as Entity+supporting properties: SelectList or MultiSelectList and either a string or List<string>. I'll also use a ViewModel for instances where I have properties I need for the view but may not necessarily need in the entity (database).
Http Get controller methods are straightforward ActionResults with return View(repository.FetchNewViewModel()) for Create or repository.FetchModelById(id) for Edit. In both instances I'm initializing my entities before passing them to the view.
Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) and Edit(ViewModel model) are the Http Post controller methods of Create and Edit. My Edit view has a hidden input field for EntityId to pass it back and forth.
By the time the Http Post method has the viewmodel, I lose all Entity.Relation and ViewModel.(Multi)SelectList values. I have to rebuild the object if I want my view to display properly:
`
try
{
var tags = model.TagIds; // List<string> or <int> depending on your Id type
if (model.TagsList == null) // It will be
{
model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList
}
if (!ModelState.IsValid)
{
return View(model);
}
_repository.Add(model.Article, tags); // or Save() for Edit
}
catch
{
return View(model); // Generally means something screwed in the repository class
}
return RedirectToAction("Index");
`
There is maybe 30% of my entity base using a ViewModel so I definitely only use it as needed. If you have complex views and model data in most instances you can probably break it down to smaller views.
Right now i´m working on a large project using S#arp Architecture and im also using the approach:
Model -> ViewModel -> Model
I use the ViewModel for the Binding part and Validations, the other approach is to use the Model Directly (with tryUpdateModel / UpdateModel which we used during the prototype develop) but for complex scenarios we end up handling special situation like SelectLists/Checkbox/Projections/HMAC Validations in a little ViewModel anyway and using a lot of Request.Form["key"] =( , the other drawback is handling the errors situations where you want to repopulate the form with the user input, i found it a little more complicated using the Model directly (using a ViewModel we take a lot of advantage of ModelState attempted value, saving us a couple of trips to the DB, anyone who have faced this scenario will know what i mean).
This approach is a bit time consuming, just like you said, you end up matching properties, but in my opinion is the way to go for complex forms.
It worth mentioning that we just use ViewModels for the Create/Edit scenarios, for almost everything else we use directly the model.
I have not use autommapers so far, but definitely i ll give it a try.

Using Include and Exclude in asp.net mvc binding OR creat a new subset object?

Does it make sense create an object that contains only those properties that the user will input on the webpage, use that for binding in the controller, and then map to the full Entity Object? Or should you just use the entity object, and use Include and Exclude to make restrictions on what gets bound on input?
I have come to like the idea of using interfaces to segregate which properties should be included when the object is updated.
For example:
To create and update an person object:
interface ICreatePerson
{
string Name { get; set; }
string Sex { get; set; }
int Age { get; set; }
}
interface IUpdatePerson
{
string Name { get; set; }
}
class Person : ICreatePerson, IUpdatePerson
{
public int Id { get; }
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
Then, when binding model, just use the appropriate interface as the type and it will only update the name property.
Here is an example controller method:
public ActionResult Edit(int id, FormCollection collection)
{
// Get orig person from db
var person = this.personService.Get(id);
try
{
// Update person from web form
UpdateModel<IUpdatePerson>(person);
// Save person to db
this.personService.Update(person);
return RedirectToAction("Index");
}
catch
{
ModelState.AddModelErrors((person.GetRuleViolations());
return View(person);
}
}
See this article (and the comments) for a very good discussion of the options.
I recommend using a separate presentation model type in most cases. Aside from the issue of binding (which is important, but there are other ways around this issue), I think that there are other reasons why using presentation model types is a good idea:
Presentation Models allow "view-first" development. Create a view and a presentation model at the same time. Get your user representative to give you feedback on the view. Iterate until you're both happy. Finally, solve the problem of mapping this back to the "real" model.
Presentation Models remove dependencies that the "real" model might have, allowing easier unit testing of controllers.
Presentation Models will have the same "shape" as the view itself. So you don't have to write code in the view to deal with navigating into "detail objects" and the like.
Some models cannot be used in an action result. For example, an object graph which contains cycles cannot be serialized to JSON.

Resources