Extremely basic question about best practice in MVC when binding drop down lists.
This inst a real world example but a basic example that explains my question:
Take the following model
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int ID {get;set;}
public string Make {get; set;{}
public string Model {get; set;}
}
Then assume that these get flattened into a view model:
public class IndexViewModel
{
public int PersonID;
public string Name;
public int SelectedCarID;
public SelectList<Cars> Cars;
}
In my constructor I have an index method:
[HttpGet]
public ActionResult Index()
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == 1);
var vm = new IndexViewModel(){
Name = person.Name,
SelectedCarID = person.Car.ID,
};
return View(vm);
}
Now, Assume that the person that is returned from the context has NO car record when the page first loads.
The view has a line :
#Html.DropDownListFor(m=>m.SelectedCarID, Model.Cars)
When the form is submitted it is picked up by the action :
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == model.PersonID);
var car = _ctx.Cars.FirstOrDefault(x=>x.ID == model.SelectedCarID);
person.Name = model.name;
person.Car = car;
_ctx.SaveChanges();
}
Now that is the way I have done it for ages, I started using EF back when LINQ to SQL was taking off and I have always created my models like that as I was under the imperssion that it was the recommended way.
After a discussion with another developer today I am not sure if this is stil the best way? It has always irked me that I need to do a lookup against the database to get the Car record out just so that I can update the record.
My questions are:
What is the best way to achive what I have described above?
Is the above correct?
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys in the model)?
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net and furious googling seems to indicate that you cant)?
This really ins't the place for Best Practices sort of questions (that would probably be Code Review).
However some notes initially.
Keep your domain objects in the domain
The first thing that stood out to me was the SelectList<Car> property. Where it appears as your Car entity is actually a domain entity. A domain entity should not be exposed to the UI for multiple reasons.
Entity framework proxy classes monitor changes to properties that can be inadvertently saved.
Re-factoring of domain entities requires re-factoring of UI Code.
Domain entities typically contact properties you would not like exposed or otherwise.
Serialization of the Domain Entities will also serialize navigation properties and (mostly likely) cause circular reference errors.
Your question
Given the above you know have your answer, you will have to do a lookup for an entity based on your criteria from your View Model. Your view model should not have any understanding of the data context. It is in fact a View Model not a Domain Entity. By telling your View Model to interact with your data contexts you have no separation between your Data Access layers and your Presentation layers.
Don't make your controller manage data access as well
Your controller has a lot of work to-do, managing data access shouldn't be one of them. Doing so you have infarct coupled your Presentation Layer with your Data Access layer. Now as this is an example its easy to forgive however re factoring your data access layer will have direct consequences to your Presentation layer. I would suggest places a Services layer in between your data access layer and the presentation layer.
Ok All this in practice how does it look.
This is my personal approach here but will look at decoupling the data layer from the Presentation layer, no domain objects passed to the Presentation layer and using services to broker the transactions to the data layer.
Sample Service
This service is responsible for handling the interaction between the data layer and presentation (note mock repositories).
public class SampleService
{
public SampleService()
{
_dbContext = new SampleContext();
}
readonly SampleContext _dbContext;
public virtual Person GetPersonById(int id)
{
return _dbContext.Persons.FirstOrDefault(x => x.ID == id);
}
public virtual Car GetCarById(int id)
{
return _dbContext.Cars.FirstOrDefault(x => x.ID == id);
}
public virtual IList<Car> GetAllCars()
{
return _dbContext.Cars.ToList();
}
public virtual void UpdatePerson(Person person)
{
if (person == null)
throw new ArgumentNullException(nameof(person));
_dbContext.SaveChanges();
}
public virtual void UpdateCar(Car car)
{
if (car == null)
throw new ArgumentNullException(nameof(car));
_dbContext.SaveChanges();
}
}
Does this appear to be more work, absolutely does but better to implement your service now than have to do it later. What we also achieve is one location to update if we wish to change any queries or interaction methods.
IndexViewModel
As we have agreed we are no longer passing the car object to the SelectList. Infact we only need to construct a basic IList<SelectListItem> and populate this from our controller.
public class IndexViewModel
{
public IndexViewModel()
{
AvailableCars = new List<SelectListItem>();
}
public int PersonID { get; set; }
public string Name { get; set; }
public int SelectedCarId { get; set; }
public IList<SelectListItem> AvailableCars { get; set; }
}
Controller
Now our controller is pretty simple to wire up.
[HttpGet]
public ActionResult Index()
{
var person = sampleService.GetPersonById(1);
var model = new IndexViewModel
{
Name = person.Name,
PersonID = person.ID,
SelectedCarId = person.Car.ID
};
model.AvailableCars = sampleService.GetAllCars()
.Select(car => new SelectListItem
{
Text = $"{car.Make} - {car.Model}",
Value = car.ID.ToString()
})
.OrderBy(sli => sli.Text)
.ToList();
return View(model);
}
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = sampleService.GetPersonById(model.PersonID);
if(person != null)
{
person.Name = model.Name;
//only update the person car if required.
if(person.Car == null || person.Car.ID != model.SelectedCarId)
{
var car = sampleService.GetCarById(model.SelectedCarId);
if (car != null)
person.Car = car;
}
sampleService.UpdatePerson(person);
}
return View();
}
View Drop Down list
#Html.DropDownListFor(m => m.SelectedCarId, Model.AvailableCars)
If you compare your code to my code I have actually added more code to the solution, however removes a lot of coupling and dependencies that could become hard to manage in larger applications.
Now back to your original questions.
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys
in the model)?
No, you should be doing a lookup for that entity (car) outside of the Model. The model should not be aware of the data context.
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
NO, your model should not be aware of the data context, therefore you do not need to define foreign keys (in a data context sense) leave that to your controller and services.
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net
and furious googling seems to indicate that you cant)?
You could, but you don't want to. Our Car entity is a domain entity and we dont want to expose the entity to the UI (Presentation). Instead we will use other classes to expose what properties are bound. In this example a simple IList<SelectListItem> was more than sufficient.
Related
I'm getting the following content when I invoke my API. It kind of breaks up in the middle when the tenant entity that member is linked to, will start listing its member entities.
{
"id":"00000000-7357-000b-0001-000000000000",
"tenantId":"00000000-7357-000a-0001-000000000000",
"userName":"user1",
"tenant":{
"id":"00000000-7357-000a-0001-000000000000",
"name":"First Fake Org",
"members":[
I configured the lazy loading like this.
services.AddDbContext<Context>(config => config
.UseLazyLoadingProxies()
.UseSqlServer(Configuration.GetConnectionString("Register")));
How should I change the code so that the lazily loaded entities don't get served? I was hoping that it would simply return an empty list to the client. Should I use a DTO for that purpose and not return from the DB like this? There's talk about not using lazy loading for APIs at all here.
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
Member output;
output = Context.Members
.Include(e => e.Tenant)
.Single(e => e.UserName == userName);
return Ok(output);
}
I'm not sure what to google for and all the hits I got were pointing to the UseLazyLoadingProxies() invokation.
This will probably be somewhat long winded: But here goes.
It sounds like you have Entities which look something like:
public partial class Member
{
public virtual long Id { get; set; }
public virtual List<Tenant> Tenants { get; set; } //tables have fk relationship
}
public partial class Tenant
{
public virtual long Id { get; set; }
public virtual List<Member> Members{ get; set; } //tables have another fk relationship?
}
And then for this method:
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
Member output;
output = Context.Members
.Include(e => e.Tenant)
.Single(e => e.UserName == userName);
return Ok(output);
}
I see a few issues, but I'll try to keep it short:
I wouldn't have the controller do this directly. But it should work.
What I think you over looked is exactly what the .Include statement does. When the object is instantiated, it will get all of those related entities. Includes essentially converts your where statement to a left join, where the foreign keys match (EF calls these navigation properties).
If you don't want the Tenant property, then you can omit the .Include statement. Unless this is meant to be more generic (In which case, an even stronger reason to use a different pattern and auto mapper).
Hopefully your database doesn't truly have a FK relationship both ways, if it does, fix that ASAP.
The next issue is that you might not want a list of child properties, but it is in the model so they will be "there". Although your List Tenants might be null. And while this might be fine to you, right now. As a general rule when I see an API returning a property, I expect something to be either not there (This Member doesn't have tenants) or something is wrong, like perhaps there is a second parameter I missed. This probably isn't a problem 93.284% of the time, but it is something to be mindful of.
This starts to get into why an AutoMapper is great. Your Database Models, Business Models and views are likely different. And as much as you shouldn't return the database models directly. Taking control of how the data is represented for each part of the application is a great idea.
You could reduce the code easily, and remove the navitation properties:
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
return Ok(Context.Members
.Include(e => e.Tenant)
.Single(e => e.UserName == userName));
}
But again, a business layer would be better:
[HttpGet("test1/{username}"), AllowAnonymous]
public IActionResult GetStuff(string userName)
{
return Ok(MemberRepository.GetMember(userName));
}
The main point I'd stress though, is creating a view model.
For example, Let's say a user Detail:
public class MemberDetail
{
public string UserName {get; set;}
public long UserId { get; set; }
public string FullName { get; set; }
}
This way the view always receives exactly what you want to see, and not the extra data. Add this with the fact that you can know exactly how every use of Member to MemberDetail will map.
I have my entity as:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I have my UserViewModel as
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I am using these as below in my controller:
//This is called from my view via ajax
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
MyRepository.UpdateUser(uv);
}
My UpdateUser in repository class is as below:
public void UpdateUser(User u)
{
var user = GetUserDetails(u.Id);
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
How can I correctly map UserViewModel in my controller to my entity
By using AutoMapper you can do something like:
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UserViewModel , User>();
});
User u = config.CreateMapper().Map<User>(uv);
MyRepository.UpdateUser(u);
}
Or manually :
public void Save(UserViewModel uv)
{
User u = new User()
{
Id = uv.Id
Name = uv.Name;
Address = uv.Address;
};
MyRepository.UpdateUser(u);
}
Which is not good to do it manually if you change your view-model and your model then you should change your code also, but with Automapper you don't need to change the code.
EDIT1:
This is not good idea to use model-view in repository (DataAccess Core) so it would be better to keep your public void UpdateUser(User u) and don't change it, in outside it is better to pass user to UpdateUser not UserViewModel like what you have done before.
EDIT2:
In my opinion non of answered posts doesn't related to SOC (Separation on concerns) even mine...
1- When I passed UserViewModel I've violated the SOC ....
2- In the other side if I got User in Peresentation layer directly I also violated the SOC.
I think the best way is a middle layer as proxy....
Presentation <----> Proxy <----> Repository.
Your repository deals with objects of type User, so you need to map the values back to an instance of that type and then make the call.
Assuming you have a method to get the user called GetUser:
public void Save(UserViewModel uv)
{
var user = MyRepository.GetUser(uv.Id);
user.Name = uv.Name;
user.Address = uv.Address;
MyRepository.UpdateUser(user);
}
You can then save the changes in your repository class. You can attach the object to make sure there are no issues if the object was created in a different context:
public void UpdateUser(User u)
{
_context.Users.Attach(u);
_context.Entry(u).State = EntityState.Modified;
_context.SaveChanges();
}
You are doing the mapping of property values(view model->enity model) inside your repositories UpdateUser method. So use the view model class (UserViewModel) as the parameter type of that.
public void UpdateUser(UserViewModel u)
{
// Get the entity first
var user = GetUserDetails(u.Id);
// Read the property values of view model object and assign to entity object
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
Now from your Save method ,you can pass the view model object to this method.
This will fix your compile time error (which is your current problem in the question), but be careful about what classes you are using in what layers. If you are too much worried about using a view model class in your data access layer, you can do that in a middle service layer. But then you are getting the entity model in that layer and doing the update there.
Remember, there is no definite answer for that question. Use the approach you think is readable and consistent with the project/ team. Often times, i tend to use the term "Common DTO classes" than "View models" so i can peacefully pass those around to another layer. I keep them in a separate project (called Common DTO) and this will be cross cutting across other projects. That means i will add a reference to this Common DTO project in my Web/UI layer and my data access/service layer and use those as needed.
I'm having trouble grasping the proper way to create view models and save that info back to the database using Entity Framework, and I can't seem to find the info I'm looking for, so please forgive me if I have overlooked it.
I came across this post here and he seems to be asking the same question but doesn't get an answer.
My main questions are,
For editing purposes, If I have a ProductModel model that has a Warranty model relationship, should I be using virtual property Warranty in the view model or should I be using int WarrantyId?
If I should be using a virtual property, why doesn't this code save the Warranty properly?
Do I need to explicitly flag or populate the Warranty for update?
Please not this does populate my edit view and select lists as intended.
My (simplified) code is setup as follows:
Model:
public int ModelId{ get; set; }
public int ModelNumber { get; set; }
public virtual Warranty Warranty { get; set;}
View Model:
public int ModelId { get; set; }
[Required(ErrorMessage = "Model Number required")]
[StringLength(25, ErrorMessage = "Must be under 25 characters")]
[Display(Name="Model Number")]
public string ModelNumber { get; set; }
//related objects and necesary properties
public virtual Warranty Warranty { get; set; }
public IEnumerable<SelectListItem> WarrantySelectListItems { get; set; }
Controller (GET):
public ActionResult Edit(int? id)
{
//check the id
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//get the model and make sure the object is populated
var model = _modelService.GetModel(id.Value);
if (model == null)
{
return HttpNotFound();
}
//pass our entity (db) model to our view model
var editModelModel = new EditModelModel();
editModelModel.InjectFrom(model);
//warranty select list
editModelModel.WarrantySelectListItems = WarrantySelectList(editModelModel.Warranty.WarrantyId);
//option multi select list
editModelModel.OptionSelectListItems = OptionSelectList();
return View(editModelModel);
}
Controller (POST) (work in progress):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditModelModel editModelModel)
{
if (!ModelState.IsValid)
{
return View(editModelModel);
}
var modelEntity = new Model();
modelEntity.InjectFrom(editModelModel);
_modelService.Update(modelEntity);
_unitOfWork.Save();
return RedirectToAction("Index");
}
View (simplified):
<div class="form-group">
#Html.Label("Warranty", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.Warranty.WarrantyId, Model.WarrantySelectListItems, "--Select--")
#Html.ValidationMessageFor(model => model.Warranty.WarrantyId)
</div>
</div>
Again, I just want to know the proper/best way to set up these viewmodels and models so the EF is doing as much of the work as possible. I feel like if I have to create a WarrantyId field, I'm doing something wrong, but maybe that isn't the case.
Thanks in advance. Any insight/help is greatly appreciated.
For editing purposes, If I have a ProductModel model that has a
Warranty model relationship, should I be using virtual property
Warranty in the view model or should I be using int WarrantyId?
You don't use virtual keyword for the property of your ViewModel, because the ViewModel has nothing to do with Entity Framework.
The reason to use the virtual keyword is to allow lazy loading in Entity Framework. In your case, if you add the virtual keyword for
the Warranty navigation property in the Product POCO class, you can access the Warranty property like below:
Model.Warranty.WarrantyId
And the reason it didn't save the Warranty information into your Database is because you need to define a Warranty foreign key property in the Product class.
In your case, if you're using code first approach and Product is your POCO class, just keep it simply like below:
public class Product
{
public int ModelId { get; set; }
public int ModelNumber { get; set; }
public int WarrantyId {get;set;}
[ForeignKey("WarrantyId ")]
public virtual Warranty Warranty { get; set; }
}
Then your ViewModel :
public class MyViewModel
{
public Product Product { get; set; }
public IEnumerable<SelectListItem> WarrantySelectListItems { get; set; }
}
Finally your view
#model MyViewModel
#Html.DropDownList("Product.Warranty.WarrantyId", Model.WarrantySelectListItems, "--Select--")
#Html.ValidationMessageFor("Product.Warranty.WarrantyId")
Of course, you need to change your action methods to meet the ViewModel.
For editing purposes, If I have a ProductModel model that has a Warranty model relationship, should I be using virtual property Warranty in the view model or should I be using int WarrantyId?
You shouldn't be using virtual properties in your view models. A view model simply represents the slice of data that is necessary to display a view. As you are mapping to that view model from your entities, you don't need to mark anything as virtual. See this answer, if you want to know what virtual is doing with regards to the Entity Framework.
Also, you should only be including the information necessary to render that view. So if you just need the WarrantyId in the view, then only include that.
As you're also model-binding back to the same view model in your POST action, you should be very specific about what you want your view model to represent, otherwise you leave yourself open to an over-posting attack.
I feel like if I have to create a WarrantyId field, I'm doing something wrong, but maybe that isn't the case.
It isn't the case. Each of your views should be self-contained. When you first starting using view models, one-per-view, your initial reaction is that of violating DRY. However, each view has different requirements. In terms of view models themselves, the most obvious distinction is validation. If you use entities in your views, all of those views are tied to the validation rules you've applied to your entities. (You'd also be vulnerable to over-posting if you don't want the user to be able to edit the entire entity.)
However, by having separate view models for your views, and applying the validation rules on the view models themselves, you can now have different validation requirements in your views. For example:
public class ViewAViewModel
{
[Required]
public int WarrantyId { get; set; }
}
public class ViewBViewModel
{
// No longer required.
public int WarrantyId { get; set; }
}
If you'd have included Warranty directly in both of these views, you'd have been stuck with one set of validation rules.
That aside, I'm wondering why you have this on your model (which I assume is an entity):
public IEnumerable<SelectListItem> WarrantySelectListItems { get; set; }
That doesn't belong here. This is a presentation detail, and it should not exist in your business objects. It should exist on your view model.
What you're dealing with are definitely navigation properties (the virtual properties on your model classes), and this does a good job of explaining them:
http://msdn.microsoft.com/en-us/data/jj713564.aspx
The tricky parts in defining these are really in how you set up your DbContext for the database. The official doc on this is here:
http://msdn.microsoft.com/en-us/data/jj591620
Simple parent-child relationships are pretty easy to handle, and there are additional situations (trickier) where you can define a number of models that physically come from the same row in a table, but I don't think you're dealing with that here.
The MVC part is a separate concern, and ideally you should treat it as such. Controller code should only delegate real "work" to other classes. The unit of work pattern, if you choose to use it, isn't really necessary until you get into situations where you've got a big lump of stuff to persist/edit across many tables or entity sets, with the idea that you may want it all to fail or succeed as a whole. If you're just handling simple persistence of single objects, don't even complicate it with the unit of work pattern.
The other thing to keep in mind with EF, or any ORM framework, is that it needs to track changes or compare to existing records, so the key values become super important as you work through this.
A ViewModel is a simplified view of your data that is UI aware and includes only information you need for UI rendering and User Input.
It might seem wrong to do more work - why not use the model directly? But with complex systems you end up with a lot of complexity and often you need to change the Model to accomodate the UI and it's a mess.
Also, ViewModels allow you to test the UI without having a database present and without that complexity. You really decouple UI issues and Data Modeling issues.
I usually end up NEVER using Models on the UI at all, always through ViewModel that simplifies my life in the end even if it's more work first.
So let's do a couple of changes.
View Model (Renamed to EditViewModel for clarity):
public int ModelId { get; set; }
// Removed for clarity, include needed properties in the UI
public int WarrantyId { get; set; }
public IEnumerable<SelectListItem> WarrantySelectListItems { get; set; }
Controller (GET):
public ActionResult Edit(int? id)
{
//check the id
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//get the model and make sure the object is populated
var model = _modelService.GetModel(id.Value);
if (model == null)
{
return HttpNotFound();
}
//pass our entity (db) model to our view model
var editViewModel = new EditViewModel();
editViewModel.InjectFrom(model);
// You could instead create a custom injection like FlatLoopValueInjection
// That would flatten and remove duplicates from
// Model.Warranty.WarrantyId to ViewModel.WarrantyId
editViewModel.WarrantyId = model.Warranty.Id;
//warranty select list
editViewModel.WarrantySelectListItems = WarrantySelectList(editViewModel.WarrantyId);
return View(editViewModel);
}
Custom Injection Flatten - FlatLoopValueInjection:
http://valueinjecter.codeplex.com/wikipage?title=flattening&referringTitle=Home
Controller (POST):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditViewModel editViewModel)
{
if (!ModelState.IsValid)
{
return View(editViewModel);
}
// You need to reconstruct the model itself, there are faster ways but I wanted
// to showcase the logic behind it
// I didn't do any null check or anything to simplify
// Load the model used from the database
var modelEntity = _modelService.GetModel(editViewModel.ModelId);
// You can do an InjectFrom for the other properties you need
// with custom Injection to unflatten
modelEntity.InjectFrom(editViewModel);
// Load the selected warranty from the database
var warrantyEntity = _warrantyService.GetWarranty(editViewModel.WarrantyId);
// Update the warranty of the model with the one loaded
modelEntity.Warranty = warrantyEntity;
_modelService.Update(modelEntity);
_unitOfWork.Save();
return RedirectToAction("Index");
}
Now in your view:
<div class="form-group">
#Html.Label("Warranty", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.WarrantyId, Model.WarrantySelectListItems, "--Select--")
#Html.ValidationMessageFor(model => model.WarrantyId)
</div>
</div>
As a side note, in your models and view models, you should try to never repeat prefixes in names like:
Model.ModelId
Warranty.WarrantyId
Unless it's a foreign key or value:
Model.WarrantyId
Why? It's a LOT easier to flatten/unflatten them by convention with InjectFrom:
Model.Warranty.Id => (flatten) => Model.WarrantyId => (unflatten) => Model.Warranty.Id
Also, it's a best practice. The name of the model/table already tells you the entity type, no need to repeat it.
You have to have int WarrantyId in your view model.
Than in your view
#Html.DropDownListFor(x => x.WarrantyId, Model.WarrantySelectListItems, "--Select--")
In Controller (POST) take WarrantyId (selected from dropdown) and find object from database (var warranty = db.Warranties.Where(w=>w.WarrantyId == editModelModel.WarrantyId or something like that) and that object assign to modelEntity.
I asked a previous question regarding best practices for mapping ViewModels to Entity Framework models in my controllers and was advised that my code was correct (using LINQ projection), though AutoMapper could be used in addition.
Now I feel like I need/want to move the bulk of what happens in the Controller methods to a new Service layer so I can add business logic when needed at this layer and then just have method calls in my controllers. But I am not exactly sure what to do. My ViewModels would all remain in the web project of course so what should my methods in the service layer look like and where/how do I map the ViewModels?
Here is a sample of a current GET and POST controller method:
public ActionResult Laboratories()
{
var context = new PASSEntities();
var model = (from a in context.Laboratories
select new LaboratoryViewModel()
{
ID = a.ID,
Description = a.Description,
LabAdmins = (from b in context.Users_Roles
join c in context.Users on b.User_ID equals c.ID
where b.Laboratory_ID == a.ID
select new LabAdminViewModel()
{
ID = b.ID,
User_ID = b.User_ID,
Role_ID = b.Role_ID,
Laboratory_ID = b.Laboratory_ID,
BNL_ID = c.BNL_ID,
First_Name = c.Pool.First_Name,
Last_Name = c.Pool.Last_Name,
Account = c.Account
})
});
return View(model);
}
[HttpPost]
public ActionResult AddLaboratory(LaboratoryViewModel model)
{
try
{
using (PASSEntities context = new PASSEntities())
{
var laboratory = new Laboratory()
{
ID = model.ID,
Description = model.Description
};
context.Laboratories.Add(laboratory);
context.SaveChanges();
}
return RedirectToAction("Laboratories");
}
catch
{
return View();
}
}
Your service layer should return your domain models. The controller is responsible for mapping them to a view model and return it to the view. A small example:
public ActionResult Laboratories()
{
// Get the laboratories domain models from the service layer.
var laboratories = _laboratoryService.GetLaboratories();
// Map the domain models to view models using AutoMapper.
var laboratoriesModel = Mapper.Map<List<LaboratoryViewModel>>(laboratories);
// Return view model to the view.
return View(laboratoriesModel);
}
With this approach you need a Core/Domain layer where your domain entities live. The service layer contains the business logic and interacts with the domain models (through repositories for example) and return the materialized objects to the controller. Your view models should indeed be in the Website project, as you propose.
Also check out this question where I provided an example of a similar solution.
Update
The GetLaborarties method in the service layer returns a (collection of) domain model(s):
public List<Laboratory> GetLaboratories()
{
return _db.Laboratories.ToList();
}
Now in your controller you call this method and map it to the view model. You can do this by using the Linq Select method:
public ActionResult Laboratories()
{
// Get the laboratories domain models from the service layer.
var laboratories = _laboratoryService.GetLaboratories();
var laboratoriesModel = laboratories.Select(new LaboratoryViewModel
{
// Map here..
}).ToList();
return View(laboratoriesModel);
}
Or use AutoMapper as I stated above.
Update 2
Trivial example with navigation properties for related objects:
Assume we have this domain model:
public class Category
{
public string Name { get; set; }
public string UrlName { get; set; }
// Other properties..
public virtual ICollection<Product> Products { get; set; }
}
We can create a method in the service layer:
public CategoryService : ICategoryService
{
public Category GetByName(string name)
{
return _categoryRepository.Table
.Include(c => c.Products) // Include related products
.FirstOrDefault(c => c.UrlName = name);
}
}
I configured Entity Framework that a Category contains zero or more products. With the Include method I ask Entity Framework to include the related products in the sql query. Now Products will contain all the related products for the category.
I don't think it makes sense to refactor such simple code to yet another layer, however the question makes sense in other contexts, where more complicated processing occurs, for example, you create an account, set a default password for it and assign roles which makes few inserts and possibly few selects in a single business transaction.
A service layer consists of services. Your services operate at the domain entities level.
public class AccountService
{
private PASSEntities _context { get; set; }
public AccountService( PASSEntities context )
{
this._context = context;
}
public User CreateAccount( string username, string password )
{
// implementation here
}
You access services from controllers and this is where the translation between view models and models occur so that services are unaware of the view model layer:
[HttpPost]
public ActionResult CreateUser( UserViewModel model )
{
using ( PASSEntities context = new PASSEntities() )
{
AccountService service = new AccountService( context );
service.CreateUser( model.Username, model.Password );
// return something appropriate here
}
}
I'm new to C# and ASP.NET MVC and i'm trying to understand the repository pattern. I've read a whole lot of articles, but I just don't understand how to use it. I'm currently using LINQ to SQL to access my SQL Server 2005 database and for testing purposes I created two tables. I have an Employees table and an EmployeeContacts table. The pk of both tables is UserName.
Employees
UserName
LastName
FirstName
Position
Email
Status
HireDate
EmployeeContacts
UserName
Contact1
Contact1Phone
Contact1Relationship
There is a one to one relationship between the two tables. An employee can be added, updated, and deleted and so can the data in the EmployeeContacts table.
So would I create a base repository to be used by both entities or should I create a repository for each entity separately? If anybody would be willing to show me some code that would be great.
So far, I have this Employee repository. I also have one for EmployeeContacts.
namespace MvcDirectoryLINQ.Models
{
public class EmployeeRepository
{
private TestDB_DataDataContext db = new TestDB_DataDataContext();
private UserName u = new UserName();
//
// Query Methods
public IQueryable<Employee> FindAllEmployees()
{
return db.Employees;
}
public IQueryable<Employee> FindRecentEmployees()
{
DateTime myDate = DateTime.Today.AddMonths(-6);
return from empl in db.Employees
where empl.HireDate >= myDate
orderby empl.HireDate
select empl;
}
public Employee GetEmployee(string UserName)
{
return db.Employees.SingleOrDefault(d => d.UserName == UserName);
}
//
// Insert/Delete Methods
public void Add(Employee employee)
{
// get the UserName which is created from the email
employee.UserName = u.ReturnUserName(employee.Email);
//Insert the new employee into the database
db.Employees.InsertOnSubmit(employee);
db.EmployeeContacts.InsertOnSubmit(employee.EmployeeContact);
}
public void Delete(Employee employee)
{
db.EmployeeContacts.DeleteOnSubmit(employee.EmployeeContact);
db.Employees.DeleteOnSubmit(employee);
}
//
// Persistence
public void Save()
{
db.SubmitChanges();
}
}
}
I have a class for an EmployeeFormViewModel:
namespace MvcDirectoryLINQ.Models
{
public class EmployeeFormViewModel
{
//Properties
public Employee Employee { get; private set; }
public EmployeeContact EmployeeContact { get; private set; }
//Constructor
public EmployeeFormViewModel(Employee employee, EmployeeContact employeeContact)
{
Employee = employee;
EmployeeContact = employeeContact;
}
}
}
Code for EmployeeController:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string UserName, FormCollection formValues)
{
Employee employee = employeeRepository.GetEmployee(UserName);
EmployeeContact employeecontact = employeecontactRepository.GetContact(UserName);
try
{
UpdateModel(employee);
UpdateModel(employeecontact);
employeecontactRepository.Save();
employeeRepository.Save();
return RedirectToAction("Details", new { UserName = employee.UserName });
}
catch
{
foreach (var issue in employee.GetRuleViolations())
{
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(new EmployeeFormViewModel(employee, attendingID));
}
}
In my View, i inherit from #model MvcDirectoryLINQ.Models.EmployeeFormViewModel. My Employee data saves correctly but the EmployeeContacts don't and I have no idea why.
Am I implementing the repository pattern correctly?
The main goal when using the Repository Pattern (as far as I understand it) is to decouple your application from using a specific Data Access Layer. You haven't done that here because you create I can see that your EmployeeRepository class does not implement an interface. You really want to have something like EmployeeRepository : IEmployeeRepository
Then, in your Controller code, you can pass around an IEmployeeRepository instead of working concretely with your EmployeeRepository. This will give you two benefits:
Should you ever need to switch the backend code, you only need to make another class that implements the interface.
When you go to test your Controllers, you can pass around a so called mock object that implements the interface instead of hitting the actual database, which slows your tests down and also breaks Unit Testing.
Another thing I noticed is that you spin up a DataContext inside your repository. If you wanted to make changes to multiple different types of objects you would therefore have multiple DataContexts open, which I don't think is what you want, since your changes won't be transactional. You may want to look into the Unit of Work Pattern for a solution.
When learning about a pattern, try to figure out the main benefit first before trying to implement it. In some cases it may not make sense. Please let me know if you would like me to elaborate on anything. Good luck.
So would I create a base repository to be used by both entities or should I create a repository for each entity separately?
The general rule when using the repository pattern is that there should be one repository class per each primary entity type. Can the EmployeeContacts live independently of any Employee? If so, they should have their own repository. Are them always related to an Employee? If so, manage them by using the Employee repository itself.