SO, this is what I have:
I have a model which stores the lat and lng of a geographical location, call it location with its own database and table.
public class location
{
public int Id { get; set; }
public int lat { get; set; }
public int lng { get; set; }
}
and I want a method in the line of:
function getDistance(location l){
return Math.sqrt(l.lat - this.lat).... etc etc.
}
Where should this go? Probably not model?
Does it belong to control?
Since it's pretty universal, should make a control that is not associated with a view?
If you could suggest some reading, that would be nice too....
Thanks for the advice(s) in advance!
Putting it in the model would be just fine.
public class location
{
public int Id { get; set; }
public int lat { get; set; }
public int lng { get; set; }
public double? getDistance(location l)
{
if (l != null)
{
return Math.Sqrt(l.lat - this.lat).... etc etc.
}
return null;
}
}
This will return the distance from one instance of location to another.
I know it isn't part of your question, but I think it's important to mention: it's pretty standard convention to capitalize the first letter of a class and method definition.
Firstly, do what you want, it's your code, make your own mistakes, think about why things aren't working or are working for your problem domain.
But, typically.
If your object is a View then a common practice is to use a POCO (Plain old C#/CLR Object), that is a class that only has properties that you want to bind with your display. You can have as many views of the same model(s) as you like depending on your situation. But they don't normally include any logic.
EDIT:
The Model is where all your business logic goes. The thing with the frameworks people are using is that it encourages the building of CRUD applications which essentially are just an access database on the web. Your Model is not just the entity map to your database. Your Model may use some form of persistence but it isn't necessarily the only activity it performs.
The Controller coordinates action between the client and the Model. In MVC an operation might ask the Controller for another page of data. It's the Controller's responsibility the ask the Model (or a cache, security framework or anything else) to return the required information it needs to compose the View (the answer) to the question posed to the controller.
Related
Is there a way to use the DisplayAttribute values of an entity within a view model?
public partial class Catalog
{
[Display(ResourceType = typeof(Resources), Name = "ID")]
public string ID { get; set; }
[Display(ResourceType = typeof(Resources), Name = "CatalogName")]
public string CatalogName { get; set; }
}
public class CatalogViewModel
{
private readonly Catalog _catalog;
// I want reuse Catalog.CatalogName's display values.
public String CatalogName
{
get { return _catalog.CatalogName; }
}
}
I cannot access Resources from the view model.
Typically you wouldn't use domain objects within view models the way you do it here.
The explanation why is it not a good practice (although functionally it will work) is here.
What you would need to do is to define your view model from strings and ints (primitives)
and use something like AutoMapper to map between them. In real life web site it is rare that you will have domain model mapped one to one to view model, typically view model is tailored for specific view carrying all the required information from multiple tables.
Back to your question: having view model defined according to best practices I just described, you certainly can (and should) define all annotations and validations (using fluent validation or data annotation)
Hope this helps, please let me know if not.
I wish to give a Person as defined below, the ability to print a vCard out of my system. To provide the user with privacy options, the user can select whether to show/hide certain properties. In it's simplest form, I need to have a separate table that would hold the user's choices.
I was wondering if it was possible to build this configurator table using reflection. As shown in the Person model below, I could decorate properties with a custom attribute, and then using those properties, construct and persist a model that would have a bool property for every decorated Person property.
public class Person
{
public string UserName { get; set; }
public string FirstName { get; set; }
[DisplayOnVCard]
public string LastName { get; set; }
[DisplayOnVCard]
public string Email { get; set; }
[DisplayOnVCard]
public string MobilePhone { get; set; }
}
** where [DisplayOnVCard] is a custom attribute.*
At the end of this, I expect a table in the db that would correspond to this:
public class VCardConfigurator
{
public bool LastName { get; set; }
public bool Email { get; set; }
public bool MobilePhone { get; set; }
}
This is just a sample representation of what is actually a huge entity. Which is why I hope to avoid manually mapping a bool field to each optional property.
I believe this problem domain is quite similar to how, for instance, privacy settings work on social networking sites, yes?
While I was typing this, I did ponder upon the possibility that if down the line I was to remove the attribute from one of the properties, what implications that might have. Needs some thought!
Further reading for self:
Programmatically adding properties to an MVC model at runtime
There is a huge possibility that I am galloping down a totally wrong path! If that is the case, please advice so!
#1 Update
I am not sure its possible to add or remove attributes for an instance since attributes are at the class level, but their property values can be changed (Since they are instances).
My suggested solusion
I am not sure what you mean in "I expect a table in the db that would correspond to this",
since you can't have a table in the database that contains only the columns of the non-privacy properties for each user.
You will need a dedicated table for this mapping (Lets say 'PrivacyMappings' table), with these columns:
UserId, PropertyName, IsPrivate.
When a user is added, all the properties will be added to this table with a default privacy settings (for instance, all properties are non-private by default).
You can add the properties by iterating over them and insert them as you said.
You can use the following class in entity framework:
public class PrivacyMapping
{
public int UserId {get;set;}
public string PropertyName {get;set;}
public bool IsPrivate {get;set;}
}
Adding the default privacy settings when a user being added:
// retrieve user model properties.
foreach (property in properties)
{
//iterrate over the user Properties.
context.PrivacyMapping.Add(new PrivacyMapping(user.userId, propertyName, isPrivate);
}
context.SaveChanges()
Now you can take all the user non-private properties by
context.PrivacyMapping.Where(p=>p.UserId == user.id && !IsPrivate).Select(p=>p.PropertyName);
And now you can deal with information any way you want.
For example, you can have a VCardItems class, that receive an user id/object in its c'tor and stores a dictionary of the allowed properties by their names.
public class VCardItems{
private Dictionary<string, object> properties{get;set;}
public VCardItems(User user)
{
// initiate values..
}
public object this[string name] {
get
{
if (properties.ContainsKey(name))
{
return properties[name];
}
// A private property.
return null;
}
set
{
properties[name] = value;
}
}
}
There is other options of how to use the data, for example with ActionFilter that in this case sets the private properties to null or storing the non-private data in the HttpContext.Items dictionary,
but it really up to you.
First message
Before we get into details, I wonder how you expect to use this class.
If a view (or whatever going to handle it), going to receive have a runtime-generated class for example, how you gonna handle it?
How you gonna know what properties this model has?
I have built a simple MVC3-based ticket entry site for a less-than-usable call center application and am attempting to refactor my prototype to better adhere to design patterns partly to make it more maintainable going forward but mostly as a learning exercise.
The user-facing view is a form consisting of basic user information in addition to a few panels allowing selection of various resource types. Each resource type (hardware, software, etc) is displayed in the same way: using dual, filterable listboxes with add/remove buttons, an optional “justification” textarea that conditionally displays if a requested resource requires justification, and general comments.
I have built the following ViewModel for the individual panels:
public class RequestableList
{
// list of requestable items ids requiring justification
private List<string> _restrictedItems = new List<string>();
public List<string> RestrictedItems
{
get { return _restrictedItems; }
set { _restrictedItems = value; }
}
// the key-value pairs from which to populate available items list
private Dictionary<string, string> _availableItems = new Dictionary<string, string>();
public Dictionary<string, string> AvailableItems
{
get { return _availableItems; }
set { _availableItems = value; }
}
// item ids requested by user
private List<string> _requestedItems = new List<string>();
public List<string> RequestedItems
{
get { return _requestedItems; }
set { _requestedItems = value; }
}
}
The main ViewModel is then comprised of multiple RequestableLists as necessary:
public class SimpleRequestViewModel
{
public UserInfo userInfo { get; set; }
public RequestableList Software {get;set;}
public RequestableList Hardware {get;set;}
public RequestableList Access {get;set;}
public string SoftwareAdditionalInfo { get; set; }
public string HardwareAdditionalInfo { get; set; }
public string AccessFileMailShare { get; set; }
public string AccessAdditionalInfo { get; set; }
public string SoftwareJustification { get; set; }
public string HardwareJustification { get; set; }
public string AccessJustification { get; set; }
public string Comment { get; set; }
}
I have created a strongly typed view for SimpleRequestViewModel (and its variant) and a strongly typed EditorTemplate for RequestableList that wires up the dual listboxes, filtering, and jquery. All renders well and is working but the code currently smells.
When posting to the controller, if the model is valid I must translate it into a readable text description in order to create a new ticket in in the call center app. It doesn’t feel right to have the controller performing that translation into readable text but I run into hurdles when trying to design another class to translate the viewmodels.
Only the selected item values are posted so before translating the request into text I must first lookup the appropriate text for the provided values (they are required in description). The controller is currently the only object that has access to the call center data model for this lookup query.
There are 2 similar ViewModels containing varying combinations of RequestableLists so any translator must be able to translate the various combinations. One has only Hardware and Software, another may have Hardware Software, and a few more RequestableLists.
I considered overriding ToString() directly in the ViewModel but didn’t like that business logic (conditional rendering) there, and again, once posted, the ViewModel doesn’t contain the text for the selected items in the listbox so it would need access to the data model.
The translation of posted values to text as it is currently handled in the controller smells as it’s handled in a switch statement. The controller takes each posted RequestableList and populates the original “Available” fields before it builds the new ticket description.
switch (requestCategory)
{
case RequestableCategory.Software:
itemList = sde.GetSoftware();
break;
case RequestableCategory.Hardware:
itemList = sde.GetHardware();
break;
case RequestableCategory.Access:
itemList = sde.GetAccess();
break;
case RequestableCategory.Telecom:
itemList = sde.GetTelecom();
break;
default:
throw new ArgumentException();
}
So, my question(s):
What patterns are techniques would you recommend for performing the posted viewmodel to ticket description translation?
How do you typically handle the “only posts value” issue with select boxes when you need the text as well as the value?
Is there a better way for me to be approaching this problem?
Again, I am hoping this is a learning experience for me and am more than willing to provide additional information or description if needed.
A few suggestions:
Abstract the logic that does the call center submission into its own class. Provide (from the controller) whatever dependencies it needs to access the call center DB. Have different methods to handle the various types of view models using overloading. Presumably the descriptions come from the DB so you can extract the description from the DB based on the value in this class. This class could also take responsibility for building your view models for the display actions as well. Note that with this pattern the class can interact with the DB directly, through a repository, or even via web services/an API.
Use a repository pattern that implements some caching if performance is an issue in looking up the description from the DB the second time. I suspect it won't be unless your call center is very large, but that would be the place to optimize the query logic. The repository can be the thing that the controller passes to the submission class.
If you don't need to access the DB directly in the controller, consider passing the broker class as a dependency directly.
It might look like:
private ICallCenterBroker CallCenterBroker { get; set; }
public RequestController( ICallCenterBroker broker )
{
this.CallCenterBroker = broker;
// if not using DI, instantiate a new one
// this.CallCenterBroker = broker ?? new CallCenterBroker( new CallCenterRepository() );
}
[HttpGet]
public ActionResult CreateSimple()
{
var model = this.CallCenterBroker.CreateSimpleModel( this.User.Identity.Name );
return View( model );
}
[HttpPost]
public ActionResult CreateSimple( SimpleRequestViewModel request )
{
if (Model.IsValid)
{
var ticket = this.CallCenterBroker.CreateTicket( request );
// do something with ticket, perhaps create a different model for display?
this.CallCenterBroker.SubmitTicket( ticket );
return RedirectToAction( "index" ); // list all requests?
}
return View();
}
I'm still learning, but with the stackoverflow commnuties help, I've been able to get closer and closer.
What I have right now is a View "Index.aspx":
System.Web.Mvc.ViewPage<Data.Models.GetDealsModel>
The Model:
public class GetDealsModel
{
// set up the model
public string DealId { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Logo { get; set; }
public string Website { get; set; }
public string TotalRows { get; set; }
}
And the controller:
public ActionResult Index()
{
LinqToDealsDataContext db = new LinqToDealsDataContext();
XElement xmlTree = XElement.Parse("<Request><ZipCode>92612</ZipCode></Request>");
var deals = db.spSearchDeals(xmlTree);
return View(deals);
}
And with this configuration I'm now getting this error:
The model item passed into the dictionary is of type 'System.Data.Linq.SqlClient.SqlProvider+SingleResult`1[Data.Models.spSearchDealsResult]', but this dictionary requires a model item of type 'Data.Models.GetDealsModel'.
I'm guessing that there's an issue connecting my Controller to my Model... I'm not sure why. PLEASE help me connect this final peice.
NOTE: I do understand that eventually I should separate my logic in the controller into a Repository Pattern, but for now, this will do.
You need to translate the data coming back from this call:
var deals = db.spSearchDeals(xmlTree);
into a GetDealsModel type. So something like:
GetDealsModel dealsModel = new GetDealsModel()
{
DealId = deals.DealId,
StreetAddress = deals.StreetAddress,
....
};
return View(dealsModel);
The reason being that your View is strongly typed to take a GetDealsModel, but your deals variable is not of that type and it gives you that exception when you pass it to the View.
You should create object of type GetDealsModel, but your DB Query returns object of type Data.Models.spSearchDealsResult. Try something like:
return new GetDealsModel
{
DealId = deals.Id,
// other fields here
}
Add to your learning curve list the following items:
Repository Pattern
Ask yourself the following question: Why do I need a service layer?
Read Steven Sanderson's book. It teaches you to think in MVC.
The above applies to your problems because your issues are clearly related to having code in your Controllers that should be in your Model (ie, data access code should be in a repository class). Ie, you are not thinking in MVC.
Your model should include the necessary repository classes, eg, DealRepository.
You need a Service class to map the objects your repository digs out of your database to your model class: that way conversion problems are encapsulated into the Service Layer code.
If you do this, you can then write in your controller:
public ActionResult Index()
{
return(DealService.GetByZipcode(92612));
}
Where DealService.GetByZipcode basically just maps DealRepository.GetByZipcode(92612) to your model class and returns the mapping result.
The DealRepository.GetByZipcode method would be roughly:
public static DealEntity GetByZipcode(string zip)
{
LinqToDealsDataContext db = new LinqToDealsDataContext();
XElement xmlTree = XElement.Parse("<Request><ZipCode>" + zip + "</ZipCode></Request>");
var deals = db.spSearchDeals(xmlTree);
return deals;
}
The DealEntity class is just whatever Linq gives you for your table.
The reason WHY for all this:
The reason for this structure is as follows:
a. All you data access code is in one place: DealRepository. You can test and debug that independently of everything else.
b. The mapping code is all in one place: DealService. You can test and debug that independently of everything else.
c. In other words, you need to properly separate your concerns.
The problem with your existing code is precisely that you have NOT separated concerns. Ie, you have taken a dash of MVC and put it in a food processor and ended up with mush full of problems that are way more difficult to deal with than they need be.
Your model is mixed into your controller, there is no repository, no service layer.
So hold your horses just a while and take the time to read Steve Sanderson's book.
I would also try modelling a simpler problem. That xml parsing makes my head hurt even on a good day.
NOTE:
You could seriously improve your naming conventions. LinqToDealsDataContext? You're kidding, right?
I think I know the answer, but I would like to bounce around some ideas.
I would like to pass several (in this instance 2) somewhat different pieces of data to a View. My initial thought is simply to wrap-up the various objects into a containing object and pass them along that way. Then from the View, I'd have something like
var objContainer = ViewData.Model;
var thisObject = objContainer.ThisObject;
var thatObject = objContainer.ThatObject;
and these could be used independently in the Master Page and View Page.
Is that the "best" way?
I find it useful to create additional classes dedicated that are to be presented to the Views. I keep them in a separate namespace called 'Core.Presentation' to keep things organized. Here is an example:
namespace Core.Presentation
{
public class SearchPresentation
{
public IList<StateProvince> StateProvinces { get; set; }
public IList<Country> Countries { get; set; }
public IList<Gender> Genders { get; set; }
public IList<AgeRange> AgeRanges { get; set; }
}
}
Then I make sure that my View is a strongly typed view that uses the generic version of that presentation class:
public partial class Search : ViewPage<SearchPresentation>
That way in the View, I can use Intellisense and easily navigate through the items.
Yes, the class that you specify as the model can be composed of other classes. However, why not just use the dictionary like so:
ViewData["foo"] = myFoo;
ViewData["bar"] = myBar;
I think this is preferable to defining the model as a container for otherwise unrelated objects, which to me has a funny smell.
I've got the same dealie going on. Here's my solution (may not be the best practice, but it works for me).
I created a number of "Grouping" classes:
public class Duo<TFirst,TSecond> { /*...*/ }
public class Trio<TFirst,TSecond, TThird> { /*...*/ }
and a factory object to create them (to take advantage of type inference... some of the TFirsts and TSeconds and TThirds can be LONG)
public static class Group{
public static Duo<TFirst, TSecond> Duo(TFirst first, TSecond second) {
return new Duo<TFirst, TSecond>(first, second);
}
/*...*/
}
It gives me type safety and intellisense with a minimum of fuss. It just smells because you're grouping together classes that essentially have no real relation between them into a single object. I suppose it might be better to extend the ViewPage class to add a second and third ViewModel, but the way I did it takes lots less work.