I'm trying to expand beyond the "one-to-one" mapping of models to controllers to views that most mvc(3) tutorials offer.
I have models for a person (operator) and the person's picture. In the database they would correspond to Operator and OperatorPicture tables.
public class Operator
{
public Operator()
{
this.OperatorPictures = new HashSet<OperatorPicture>();
}
[DisplayName("Operator ID")]
public int OperatorID { get; set; }
[Required]
[DisplayName("First name")]
[StringLength(20, ErrorMessage = "The first name must be 20 characters or less.")]
public string OperatorFirstName { get; set; }
[Required]
[DisplayName("Last name")]
[StringLength(20, ErrorMessage = "The last name must be 20 characters or less.")]
public string OperatorLastName { get; set; }
public virtual ICollection<OperatorPicture> OperatorPictures { get; set; } // Nav
}
public class OperatorPicture
{
[DisplayName("Operator Picture ID")]
public int OperatorPictureID { get; set; }
[DisplayName("Operator ID")]
public int OperatorID { get; set; }
[DisplayName("Picture")]
public byte[] Picture { get; set; } // 100x100
[DisplayName("Thumbnail")]
public byte[] Thumbnail { get; set; } // 25x25
public virtual Operator theoperator { get; set; } // FK
}
In most views I would present them together. A list of operators would include a thumbnail picture if it exists. Another screen might show the person's detailed information along witht the full-sized picture.
Is this where viewmodels come into play? What is an appropriate way to use them?
Thanks,
Loyd
Definitely a time for using them. ViewModels are just a way of packaging up all the different bits that go into a view or partialview.
Rather than have just the repository pattern, I have repositories and services. The service has a Get property called ViewModel, that can be called from the controller. EG:
// initialise the service, then...
return View(service.ViewModel)
You can see a more detailed description of my approach in the accepted answer for the following SO question:
MVC Patterns
Note that I am using dependency injection in that code.
It may not be a totally orthodox or canonical approach, but it results in very clean controller code and a easy to use pattern.
Related
We have been implementing our ERD in EF.
Code First for Project
public class Project
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProjectID { get; set; }
[Index("IX_ProjectGUID", IsUnique = true)]
[Required]
public Guid GUID { get; set; }
[MaxLength(256), Index("IX_ProjectName", IsUnique = true)]
[Required]
public string Name { get; set; }
public virtual ICollection<UserAttribute> UserAttributes { get; set; }
}
Code First for UserAttributes
public class UserAttribute
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserAttributeID { get; set; }
[Index("IX_Project_Atttribute_Name", 1, IsUnique = true)]
public int ProjectID { get; set; }
[ForeignKey("ProjectID")]
public virtual Project Project{ get; set; }
[Index("IX_Project_Atttribute_Name", 2, IsUnique = true)]
public int AttributeTypeID { get; set; }
[ForeignKey("AttributeTypeID")]
public virtual SystemUserAttribute SystemUserAttribute { get; set; }
[MaxLength(256), Index("IX_Project_Atttribute_Name", 3, IsUnique = true)]
[Required]
public string Name { get; set; }
public virtual ICollection<UserBooleanAttribute> UserBooleanAttributes { get; set; }
}
So you can see from the last line of each of those classes a 1-many bidirectional relationship is setup.
Before I introduced this 1-many collection I would of been required to todo:
var jar = context.Projects
.Where(p=>p.ProjectID==1)
.Join(
context.UserAttributes,
a => a.ProjectID, b => b.ProjectID,
(a, b) => new {a, b});
Now I only have to do:
Projects.Where(prj=>prj.ProjectID==1).Select(ua=>ua.UserAttributes).Single()
Since Lazy-Loading in effect, is there really no degradation to performance?
*Single -- Why do I need to call this or something similar like FirstOrDefault?
Looks ok. A few quirks to it, like why you are assigning both an ID and GUID to a project. Really should be one or the other.
Single seems out of place because you are selecting the UserAttributes, which could be one or more of them. Single implies there is only one, and if that was true, your design is more complex than it should be.
I assume you'll be adding navigation properties for string and integer as well, which is just fine.
The User*Attributes classes should also have navigation properties to UserAttributes as well.
I've tried analyzing your design, and all I can say is it gives me a headache. I'm going to assume there is some outside reason you've chosen the PK's you have instead of using natural keys. From a glance, UserAttributes seems poorly named. It's not user attributes, it appears to be a project's attributes (or attributes that are assignable to a user for each project). I would also ask if breaking your attributes up into 3 separate tables instead of always serialzing the value into a string is worth the headaches, because it's not likely to save you any space (with the exception of integer -- perhaps) and greatly slow down every query you need to make.
Not sure how to explain this, but here goes...
I've built a code first data model using EF 4.3. One of classes, "Address" contains typical address data, street, city, state, etc. Other classes in the model contain instances of the "Address" class.
The problem. The data will be gathered/presented using different views, some of which will require the address fields, others that will not.
I can build different view models, each having the necessary validation attributes, and copy the data back and forth between data model and view model but that seems wrong.
What am I missing? There has to be a smarter way to do this.
Thanks for your help,
Jimmy
First read these questions and their answers:
MVC: Data Models and View Models
Why Two Classes, View Model and Domain Model?
also this article could help:
ASP.NET MVC View Model Patterns
In conclusion, I think in most scenarios it's helpful to have a chubby domain model (DM) but light weight presentation models (PM) related to it. So when we want to edit only a small chunk of that fat DM, one of our PMs will raise its hand.
Imagine this class in DM:
namespace DomainModels
{
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime? DoB { get; set; }
public MyAddressDM Address { get; set; }
public string Phone { get; set; }
public IEnumerable<MyCarModel> Cars { get; set; }
//etc.
}
}
Now imagine that in one view we need to edit only Address and Phone. A light weight PM could be like:
namesapce PresentationModels
{
public PersonAddressPhone
{
public int ID { get; set;}
public string FullName { get; set;}
public string AddressSteet { get; set; }
public string AddressCity { get; set; }
public string AddressState { get; set; }
public string AddressZipCode { get; set; }
public string Phone { get; set; }
}
}
and in another view we need to add/remove cars for a person:
namesapce PresentationModels
{
public PersonCars
{
public int ID { get; set;}
public string FullName { get; set;}
public IEnumerable<PMCar> Cars { get; set;}
}
}
Mapping between DO and PM is the golden piece of this puzzle. Be sure to take a look at AutoMapper.
I'm trying to create some complex form scheme in a .NET MVC 3 project (or so I think). I'll show a wireframe so that you can understand better.
As you can see from the pic above, it's a form to create a new Car Dealership. I can choose from many available car's manufacturers (in the Add a tab), and each selection creates a new tab in the panel underneath it. In each panel there's a html fieldset, and I can add some new cars on it.
In the end, I want to submit all the form, which comprehends the data from the dealership (e.g. name), the car's brands that it has and the cars available for each brand. How can I do it with MVC 3? I don't know how exactly should I instantiate the model Car Dealership and its respective View. What should the cars in the panels be? An array of inputs? And how can I separate the inputs from one tab from the ones of another? All of this, using Razor!
My real-world scenario is a little bit more complicated, but I'll start by this so that I can try to solve the rest by myself. I'm using jQuery/jQuery UI, but I'm opened to other javascript alternatives (like Backbone, etc - I just don't know them yet). Thanks in advance!
UPDATE (1):
These are my models so far:
public class CarDealership
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ManufactorOnDealership> ManufactorsOnDealership { get; set; }
}
public class Manufactor
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ManufactorOnDealership> DealershipsOfManufactor { get; set; }
}
// Junction class: I believe it's needed because the list of Cars belongs to this relationship
public class ManufactorOnDealership
{
public int Id { get; set; }
public int CarDealershipId { get; set; }
public int ManufactorId { get; set; }
public virtual CarDealership CarDealership { get; set; }
public virtual Manufactor Manufactor { get; set; }
public virtual ICollection<Car> CarsOnDealership { get; set; }
}
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ManufactorOnDealership> Manufactors { get; set; }
}
UPDATE #3: Entire question
I have a class HB:
public class HB
{
public int Id { get; set; }
[StringLength(3000)]
public string Text { get; set; }
public Title Title { get; set; }
}
And Title:
public class Title
{
public int Id { get; set; }
public string Name { get; set; }
public int MaxChar { get; set; }
}
Before you can write a HB (which is kind of an article), you have to choose your title, so your StringLength for HB.Text can be determined. Meaning, this article can only have a certain amount of chars, deppending on what 'Title' the writer has. Example: Title1 can only write a 'HB' with 1000 chars, and Title2 can write a 'HB' with 3000 chars. So. Thats means the the StringLength has to come from Title.MaxChar. Whats the smartest way to do that?
The Title entity is prefixed data that will be stored in the db.
To be crystal clear, what I want to achieve is something in the line with: [StringLength(Title.MaxChar)]
Ive done structure/design for this mechanism in Webforms a million times, my brain just cant addapt to mvc, so some help would be appreciated. Code would be even more appreciated.
Pretty sure that is not possible as written. This strikes me as trying to force business logic into the model that belongs in the controller.
In this situation, I would make the attribute on the Text property [StringLength(3000)]. In the controller, during validation, I would write something along these lines:
public ActionResult (HB model)
{
if (model.Text.Length > model.Title.MaxChar){
ModelState.AddModelError("Text", string.Format("Text for this Title cannot exceed {0} characters.", model.Title.MaxChar));
}
if (ModelState.IsValid)
{
//do stuff
return RedirectToAction("Index"); //or something
}
else
{
return View(model);
}
}
I believe this will accomplish what you are trying to do. Now, for the Title object, I'd flatten that out a bit in your model:
public class HB
{
#region Base Properties
public int Id { get; set; }
[StringLength(3000)]
public string Text { get; set; }
#endregion
#region Title Properties
public int TitleId { get; set; }
public string TitleName { get; set; }
public int TitleMaxChar { get; set; }
#endregion
}
This is assuming you need to display that information in your view. If you just need to reference it for your business logic validation, just have the TitleId property and use that to instantiate the Title object in your controller when you need it. Don't forget to make hidden inputs for each of these properties if they are not editable!
I have a S#arp MVC application with following projects/layers :
Core (Model)
Data
Web (View)
Controllers
Services
One example to show the problem:
I have a model Ticket :
public class Ticket : Entity
{
public virtual string Summary { get; set; }
public virtual string Description { get; set; }
public virtual DateTime CreateOn { get; set; }
public virtual DateTime UpdatedOn { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User AssignedTo { get; set; }
public virtual Priority Priority { get; set; }
public virtual Status Status { get; set; }
}
Its located in Core project...
To my View Create(Ticket) I need a CreateTicketViewModel :
public class CreateTicketVM
{
[Required(ErrorMessage = "Required.")]
public string Summary { get; set; }
[Display(Required(ErrorMessage = "Required.")]
public string Description { get; set; }
[Required(ErrorMessage = "Required.")]
public int AssignedToId { get; set; }
[Required(ErrorMessage = "Required.")]
public int PriorityId { get; set; }
[Required(ErrorMessage = "Required.")]
public virtual int StatusId { get; set; }
public IList<User> Users { get; set; }
public IList<Priority> Priorities { get; set; }
public IList<Status> Status { get; set; }
}
Its located in Controller project...
So far so good... But in my TicketController I need fill all CreateTicketVM lists (dropdownlists in View).
So I created a TicketService :
public class TicketService : ITicketService
{
readonly IRepository<User> userRepository;
readonly IRepository<Priority> priorityRepository;
readonly IRepository<Status> statusRepository;
...
public CreateTicketVM CreateNewCreateTicketVM()
{
var _ticket = new CreateTicketVM();
_ticket.Priorities = priorityRepository.GetAll();
_ticket.Status = statusRepository.GetAll();
_ticket.Users = userRepository.GetAll();
_ticket.Categories = categoryRepository.GetAll();
return _ticket;
}
}
Its located in Service project...
The problem is with the Interface ITicketService :
public interface ITicketService
{
CreateTicketVM CreateNewCreateTicketVM();
}
It´s located in Core project... But Core Project cannot include Controller project (CreateTicketVM) ...
So, how can I handle that?
Thanks
Paul
Instead of putting your CreateNewCreateTicketVM method on your service I would create an enhanced query object in your Controllers project for this. The services layer is then only concerned with behaviour.
See my blog post:
http://www.yellowfeather.co.uk/2011/03/enhanced-query-objects-with-sharp-architecture/
and Ayende's review:
http://ayende.com/Blog/archive/2011/04/01/code-review-sharparchitecture.multitenant.aspx
Chris nailed that one. Use the query object to project straight into your view model.
When you return a ViewModel (or a DTO, name doesn't matter), filled with ILists only meant to be displayed in a view, you're already tightly coupled to your view, from a "semantic" point of view.
Aren't you over engineering something here, that can't be solved with less projects, like :
Core : Infrastructure blocks that can be shared with other apps
MyApp : Specific code for your application, ordered with namespaces
MyApp.Data
MyApp.Services (service interfaces)
MyApp.Services.Implementations (if you really want to separate)
MyApp.Web
MyApp.Web.Models
MyApp.Web.Controllers
MyApp.Web can be an MVC project, or if you want, 2 projects, one c# library with controllers, and 1 MVC project with only views.
In this case, I think it it too much to have a service layer build a ViewModel. This should be done in your controller, or better, by a tool like AutoMapper.
Service layer should have some business responsibilities : "does this user can assign ticket to this one at this precise time ?" for example.
Also, Ayende has a nice blog post about this : http://ayende.com/Blog/archive/2011/03/16/architecting-in-the-pit-of-doom-the-evils-of-the.aspx
Don't forget : KISS