I have a class Article with several properties.
I want to know whether it is possible to override the ToString method for the bool and DateTime properties so that for the booleans they print as "Yes/No" and the DateTime as custom text.
Idea is that when these properties are printed in a ListView with GridViewColumn bound to every property they don't print standard ToString values.
public class Article
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Content { get; set; }
public int NumberOfWords { get; set; }
public string Category { get; set; }
public bool CanBePublished { get; set; }
public bool Published { get; set; }
public int Length { get; set; }
public DateTime CreationDate { get; set; }
public Article() { }
public Article(string title, string author, string content, int numberOfWords, string category, bool canBePublished, int length)
{
Title = title;
Author = author;
Content = content;
NumberOfWords = numberOfWords;
Category = category;
CanBePublished = canBePublished;
Length = length;
Published = false;
CreationDate = DateTime.Now;
}
}
You can define get method to get the formatted value from those fields as shown below. Create a view model class for that and do it in this way by difining a get method. and use that property to read the data. Like Article_ViewModelObject.CreationDateVal
public class Article_ViewModel: Article
{
public string CreationDateVal
{
get
{
return CreationDate.ToString();
}
}
public string CanBePublishedVal
{
get
{
return CanBePublished ? "Yes" : "No";
}
}
}
It is not a good idea to modify your model data class to alter what you see in a view. Most platforms that display model data objects have a way of modifying what you are seeing, based on the raw data.
Depending on the stack you are using, search for modifying the view based on raw data instead. If you can show your UI code, we can be of further help.
Related
I am using .net core clean architecture along with jQuery datatable. Server-side search is enabled, but I cannot map that search param search[value] from datable to a model property in c#. I have tried the Newtonsoft JsonPropertyName attribute to map it but it fails. Below is my model code:
public class GetVotesByMeetingIdQuery : IRequest<PaginatedList<VoteCastDTO>>
{
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;
public Search Search { get; set; }
}
public class Search
{
[JsonProperty(PropertyName = "value")]
public string Value { set; get; }
[JsonProperty(PropertyName = "regex")]
public bool Regex { set; get; }
}
I am able to capture the param from the request in my controller.
[HttpGet("GetVotesByMeetingId")]
public async Task<ActionResult<PaginatedList<VoteCastDTO>>> GetVotesByMeetingId([FromQuery] GetVotesByMeetingIdQuery query)
{
var exist = Request.Query.TryGetValue("search[value]", out Microsoft.Extensions.Primitives.StringValues val);
query.Search = exist ? val.ToString() : string.Empty;
return await Mediator.Send(query);
}
but I don't want to do this as I want to keep my controller clean. Is there anyway to sort out this issue?
You can use [FromQuery] attribute on the property. It will map the parameter to the property accordingly. Also, you need to change the property type to string as you are getting the param value in string. Below is the example:
public class GetVotesByMeetingIdQuery : IRequest<PaginatedList<VoteCastDTO>>
{
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;
[FromQuery(Name = "search[value]")]
public string Search { get; set; }
}
I have a model that I'm loading into a table within a form. The records are retrieved from an Oracle DB using EF6 and loaded into the model.
I also want the user to be able to select records to delete from the database via a checkbox in each row in the form.
The function to retrieve the Attendees:
public List<WebinarAttendeesList> getAttendees(string webinarKey)
{
string connectionString = "Password=password;User Id=user;Data Source=Oracle";
List<WebinarAttendeesList> r = null;
using (webinarAttendeesListDbContext context = new webinarAttendeesListDbContext(connectionString))
{
var result = from w in context.WebinarAttendeesList
where w.webinarKey == webinarKey
orderby w.FirstPollCount, w.SecondPollCount
select w;
r = result.ToList();
}
return r;
}
Here is the model:
[Table("WEBINARATTENDEESLIST")]
public class WebinarAttendeesList {
[Key, Column("WAL_ID")]
public int wa_id { get; set; }
[Column("WAL_CLI_RID")]
public int ParticipantID { get; set; }
[Column("WAL_FULLNAME")]
public string FullName { get; set; }
[Column("WAL_EMAIL")]
public string Email { get; set; }
[Column("WAL_JOINTIME")]
public string JoinTime { get; set; }
[Column("WAL_TIMEINSESSION")]
public string TimeInSession { get; set; }
[Column("WAL_LEAVETIME")]
public string LeaveTime { get; set; }
[Column("WAL_FIRSTPOLLCOUNT")]
public int FirstPollCount { get; set; }
[Column("WAL_SECONDPOLLCOUNT")]
public int SecondPollCount { get; set; }
[Column("WAL_ATTENDEDWEBINAR")]
public int AttendedWebinar { get; set; }
[Column("WAL_MAKEUP")]
public int Makeup { get; set; }
[Column("WAL_COMMENTS")]
public string Comments { get; set; }
[Column("WAL_REGISTRANTKEY")]
public string RegistrantKey { get; set; }
[Column("WAL_WEBINARKEY")]
public string webinarKey { get; set; }
}
When the form is submitted, I am passing the model to a function to store the records in EF6.
public ActionResult PostAttendees(ICollection<WebinarAttendeesList> attendees)
{
foreach (WebinarAttendeesList attendee in attendees)
{
UpdateAttendee(attendee);
}
}
How would I edit the model to allow this delete the records that are selected and update the ones that don't have the checkbox selected?
If I put an int delete property on the model that has no Column attribute I get this exception:
ORA-00904: "Extent1"."delete": invalid identifier
I found this tutorial but I'm NOT using any helpers in the creation of the form and do not have any ViewModels and it also doesn't explain how to handle doing different things to the different records based on the checkbox: http://johnatten.com/2014/01/05/asp-net-mvc-display-an-html-table-with-checkboxes-to-select-row-items/
Is there a better way to do this?
Yes. All models properties in EF are suppose to be column. You should use NotMapped attribute if you don't want property to be treated as a 'column' in database.
I'm trying to create an object that represents the values that datatables supplies to my web api call, which I'll then route to another api that actually returns the values (to separate the datatables nonsense from the api interface).
After researching a bit on the datatables wiki I ended up with the following objects defined:
public class DataTableParameters
{
public int draw { get; set; }
public int start { get; set; }
public int length { get; set; }
public order[] order { get; set; }
public column[] columns { get; set; }
}
public class order
{
public int column { get; set; }
public string dir { get; set; }
}
public class column
{
public string data { get; set; }
public string name { get; set; }
public bool searchable { get; set; }
public bool orderable { get; set; }
public search search { get; set; }
}
public class search
{
public string value { get; set; }
public bool regex { get; set; }
}
However, when I try to use them as arguments to the DataTables api controller, it comes out null:
public DataTableResult Get(DataTableParameters parameters) //parameters is null!
{
return new DataTableResult();
}
As far as I understood it, model binding should be reading the result and applying it to my object. This is an example call to the api from the front end:
Key Value
Request GET /MVC/api/DataTables?action=Get&draw=1&columns%5B0%5D%5Bdata%5D=0&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=1&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=2&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=3&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=true&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1440437669357 HTTP/1.1
Why is my parameters object null, and how can I fix it?
Edit: I also attempted this:
public DataTableResult Get(int draw, int start, int length, column[] columns, order[] order)
{
return new DataTableResult();
}
But I get "Can't bind multiple parameters ('columns' and 'order') to the request's content."
UGH less than 10 minutes later, I need a [FromUri] attribute on the parameters.
public DataTableResult Get([FromUri]DataTableParameters parameters)
{
return new DataTableResult();
}
I have Some model, for example: RequestModel
public class RequestModel
{
public int RequestId { get; set; }
public int CategoryKey { get; set; }
public int SubCategoryKey { get; set; }
public int AreaKey { get; set; }
}
This model I use to get input from some html form.
CategoryKey, SubCategoryKey, and AreaKey, are codes for the Category, Subcategory and the Area values.
For display purposes I need to show the CategoryValue, SubCategoryValue and AreaValue. So I use the same model for my View and added the three fields to it.
public class RequestModel
{
public int RequestId { get; set; }
public int CategoryKey { get; set; }
public int SubCategoryKey { get; set; }
public int AreaKey { get; set; }
public String CategoryValue { get; set; }
public String SubCategoryValue { get; set; }
public String AreaValue { get; set; }
}
I'm thinking of separate it to the following classes:
public class RequestModel
{
public int RequestId { get; set; }
}
public class RequestInputModel : RequestModel
{
public int CategoryKey { get; set; }
public int SubCategoryKey { get; set; }
public int AreaKey { get; set; }
}
public class RequestDisplayModel : RequestModel
{
public int RequestId { get; set; }
public String CategoryValue { get; set; }
public String SubCategoryValue { get; set; }
public String AreaValue { get; set; }
}
What do you think? is it really necessary? do you do this separation between classes for input and classes for display?
The General Best Practice is that you have your Data in the model. ie information that you need to save to a file or database, you then have your business logic on a View Model, this would be things like the Fullname being a First + Surname and the like, so data that needs to be displayed but not saved
then you have your View on top that formats and styles your output
so you would have
public class PersonModel
{
string firstName;
string Surname;
DateTime DateOfBirth
}
public class PersonDisplayViewModel
{
PersonModel Model;
string FullName
{
get{return Model.firstname + " " + Model.Surname;}
}
int Age
{
get{return (DateTime.Today() - Model.DateOfBirth).TotalYears;}
}
}
public class PersonEditViewModel
{
PersonModel Model;
string FirstName {
get{return Model.FirstName;}
set{Model.FirstName = value;}
}
string Surname{
get{return Model.Surname;}
set{Model.Surname= value;}
}
DateTime DateOfBirth{
get{return Model.DateOfBirth;}
set{Model.Surname= DateOfBirth;}
}
}
The reason for this is that the same core data may be used in many places, say you have a search screen that shows Full name and age and an editing screen that lets you edit first and last name and date of birth. they have the same core data of person but they present the data in different ways. also say you were doing something for a School and wanted to make sure a students age is <16 but you also have staff that have to be >18. you can't put this age filter on the person Model as a person can be any age, but would fit perfectly well on the view model that is defining what can and can't be written to the under lying model
the same is true for views if you have a version of your app in USA you want to use American Date Format ie mm-dd-yyyy but the same app in UK would need the dd-mm-yyyy format does this have any effect on the underlying data or business logic? No it just changes the appearance of the data so goes in the veiw
My database table for buildings stores the building type as a code. In a separate lookup table the description for that code is stored.
How should I design my ViewModel and where will I need to make the call to get the associated description value?
I sort of can see one option. I want to know if there is a better option.
BuildingViewModel
{
public string BuildingTypeCode { get;set;}
...other properties
}
Then in my view
code...
<p>#MyService.GetDescription(Model.BuildingTypeCode)</p>
...code
Am I incorrect in the way I am thinking? if I do the above I create a dependency in my View to the service?
Update 1
Working through some of the solutions offered. I seem to run into another issue. I can't access the constructor of each building directly...
public ViewResult Show(string ParcelId)
{
var result = _service.GetProperty(ParcelId);
var AltOwners = _service.GetAltOwners(ParcelId);
var Buildings = _service.GetBuildings(ParcelId);
ParcelDetailViewModel ViewModel = new ParcelDetailViewModel();
ViewModel.AltOwnership = new List<OwnerShipViewModel>();
ViewModel.Buildings = new List<BuildingViewModel>();
AutoMapper.Mapper.Map(result, ViewModel);
AutoMapper.Mapper.Map<IEnumerable<AltOwnership>, IEnumerable<OwnerShipViewModel>>(AltOwners,ViewModel.AltOwnership);
AutoMapper.Mapper.Map<IEnumerable<Building>, IEnumerable<BuildingViewModel>>(Buildings, ViewModel.Buildings);
ViewModel.Pool = _service.HasPool(ParcelId);
ViewModel.Homestead = _service.IsHomestead(ParcelId);
return View(ViewModel);
}
public class ParcelDetailViewModel
{
public IEnumerable<OwnerShipViewModel> AltOwnership { get; set; }
//public IEnumerable<ValueViewModel> Values { get; set; }
public IEnumerable<BuildingViewModel> Buildings { get; set; }
//public IEnumerable<TransferViewModel> Transfers { get; set; }
//public IEnumerable<SiteAddressViewModel> SiteAddresses { get; set; }
public string ParcelID { get; set; }
//public string ParcelDescription { get; set; }
//public int LandArea { get; set; }
//public string Incorporation { get; set; }
//public string SubdivisionCode {get;set;}
public string UseCode { get; set; }
//public string SecTwpRge { get; set; }
//public string Census { get; set; }
//public string Zoning { get; set; }
public Boolean Homestead {get;set;}
//public int TotalBuildingArea { get; set; }
//public int TotalLivingArea { get; set; }
//public int LivingUnits { get; set; }
//public int Beds { get; set; }
//public decimal Baths { get; set; }
public short Pool { get; set; }
//public int YearBuilt { get; set; }
}
My understanding is that the view model is meant for display ready data. I think the real problem here is putting model dependent logic into the view.
You can do your service lookup but keep that code in the controller. The view model should be considered display ready (save for some formatting).
class BuildingViewModel
{
public string BuildingTypeCode { get;set;}
...other properties
}
and then do the lookup before you render:
public ActionResult Building()
{
var typeCode = // get from original source?
var model = new BuildingViewModel
{
BuildingTypeCode = MyService.GetDescription(typeCode)
};
return View("Building", model);
}
Having come from a long line of JSP custom tags I dread having any code hidden in the view layout. IMO, that layer should be as dumb as possible.
I would recommend having a helper that does that, or a DisplayTemplate
public class ViewHelpers
{
public static string GetDescription(string code)
{
MyService.GetDescription(Model.BuildingTypeCode);
}
}
or
#ModelType string
#Html.DisplayFor("",MyService.GetDescription(Model.BuildingTypeCode));
More info on templates: http://www.headcrash.us/blog/2011/09/custom-display-and-editor-templates-with-asp-net-mvc-3-razor/
Both of these approaches introduce a dependency on your service but you can test/change them in one single place, instead of the whole application (plus the usage looks cleaner).