I don't know how to phrase this question properly but basically I haven an ASP.Net Application. I send the following request to the controller from my view:
http://localhost:59112/Contacts/IndexJson?current=1&rowCount=50&sort%5BLastName%5D=desc&searchPhrase=&_=1490960196673
I have written two classes that are not working 100% as follows for a structure for this request data:
public class RequestData
{
public int current { get; set; }
public int rowCount { get; set; }
public string searchPhrase { get; set; }
public IEnumerable<SortData> sortItems { get; set; }
}
public class SortData
{
public string Field { get; set; } // FIeld Name
public string Type { get; set; } // ASC or DESC
}
Then in my controller I have the following:
public JsonResult IndexJson(RequestData model)
{
/* Irrelevant code */
}
The model works and fills everything correctly except the sortItems returns null. How can I get the sortItems Field and Type defined in my class?
Since the parameter coming in from the RequestData is sort[Field]=Type.
Edit
I changed my RequestData class to this:
public class RequestData
{
public int current { get; set; }
public int rowCount { get; set; }
public Dictionary<string, string> sort { get; set; }
public string searchPhrase { get; set; }
public Guid id { get; set; }
}
Now the model holds the sort as {[Field, Type]} (an example of data).
If this is a good practice, how to I access Field and Type?
You can achieve this a number of different ways; your problem in each case was simply not following the modelbinder conventions for that data type.
First and foremost, IEnumerable is out if you intend to post back to it. It's not an indexable type, so the modelbinder will never be able to bind to it. However, using something like List instead, is just fine. Then, your param names simply need to be in the format of: ListProperty[N].Property, where N is the index. So for your situation you could have used sortItems[0].Field=LastName&sortItems[0].Type=desc, and it would have bound just fine to your model.
For using a dictionary, your names should be in the format of DictionaryProperty[N].Key and DictionaryProperty[N].Value, where again, N is the index. In your scenarion that would look like sort[0].Key=LastName&sort[0].Value=desc.
Related
How do I read data into a concrete model without knowing the case of the source column names?
I'm reading data out of table storage:
public static IEnumerable<Translation> GetTranslations(string sourceParty, string destinationParty)
{
var acc = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("RRRCacheStorageConnection"));
var tableClient = acc.CreateCloudTableClient();
var table = tableClient.GetTableReference(Environment.GetEnvironmentVariable("RRRTableCache"));
TableQuery<Translation> rangeQuery = new TableQuery<Translation>().Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition(Environment.GetEnvironmentVariable("SourcePartyColumnName"), QueryComparisons.Equal, sourceParty),
TableOperators.And,
TableQuery.GenerateFilterCondition(Environment.GetEnvironmentVariable("DestinationPartyColumnName"), QueryComparisons.Equal, destinationParty)));
return table.ExecuteQuery(rangeQuery);
}
Then I dump into a concrete model, which looks like this:
public class Translation : TableEntity
{
public Translation()
{
}
public string translatefrom { get; set; }
public string translateto { get; set; }
public string TranslationId { get; set; }
public string FieldType { get; set; }
public string sourceparty { get; set; }
public string destinationparty { get; set; }
}
However, if the source table in Azure has a field called TranslateFrom and not translatefrom (as defined in the class above), then data is not read.
How do I read data into a concrete model without knowing the case of the source column names?
You should follow conventions of UpperCamelCase in C# and lowerCamelCase for JSON/JS
So mixing conventions like you have it is a bad idea and will just cause problems with libraries like Entity, causes problems for other users on the readability side and once you start doing API calls gets completely confusing.
Update your table entity to use all UpperCamelCase. The same applies for DTO's because JSON Convert will automatically apply lowerCamelCase during serialisation and deserialazation for JavaScript while keeping your code within the C# Convention.
There is an option to tell Entity to ignore casing... but I rather provide you with an outside opinion to NOT change the conventions that where put in place by clever people... but rather fix your approach to make life easier for everyon down the line.
Assuming this standard way of writing C# (UpperCase)
public class TestClass {
public string TranslateFrom { get; set; }
}
and you return it via your ActionResult in MVC or API
public ActionResult SomeActionMethod() {
return Json(new TestClass(){TranslateFrom="z-axis"});
}
the JSON (property naming convention) will be lowerCase
{ "translateFrom" : "z-axis" }
and the other way around. You can send upper or lower case to C# MVC/API and Newtonsoft.JSON will work out. But you should stick with JavaScript convention of always lowerCase properties.
Your class should look like this.
public class Translation : TableEntity
{
public string TranslateFrom { get; set; }
public string TranslateTo { get; set; }
public string TranslationId { get; set; }
public string FieldType { get; set; }
public string SourceParty { get; set; }
public string DestinationParty { get; set; }
}
I have an MVC Model that generates JSON files, based off of user inputs, that are used as part of an automated workflow. The issue that I am having is figuring out how to change the order in which a list of objects are serialized based off of a specific property value.
Here is a simplified look at my model:
public class Ticket
{
public string TicketNumber { get; set; }
public string TicketName { get; set; }
public string ApplicationName { get; set; }
public IList<Jams> JamsList { get; set; }
}
public class RootObject
{
public Ticket ChangeTicket { get; set; }
}
public class JamsDestination
{
public string Dev { get; set; }
public string QA { get; set; }
public string Prod { get; set; }
}
public class Jams
{
public string TFSLocation { get; set; }
public string FileName { get; set; }
public string JamsType { get; set; }
public JamsDestination JamsLocation { get; set; }
}
(I am using Newtonsoft.Json and the SerializeObject() function in the post section of my controller)
JamsType is a drop down list populated from a sql table (Variable, Job, Trigger, and Box). What I am trying to do is ensure that any Jams change (in the list: JamsList) is serialized in an order that ensures that all Jams changes of JamsType = Box are serialized last, in order to ensure that it will run properly as a part of our automated workflow. Is there any way to accomplish this without setting up some form of Javascript function in the view to reorder them before they are indexed? (My view is a dynamic table setup so it is not guaranteed that there even will be any Jams changes each time, let alone how many are associated with a ticket).
I realized that I just needed to add Linq logic into my controller PRIOR to serializing the JSON file by doing the following:
ticket.JamsList = ticket.JamsList.OrderBy(jams => jams.JamsType == "Box").ToList();
All this actually does is just reorder the list of Jams changes to meet my conditions before it gets serialized, rather than changing the order it serializes the list (how I thought it needed to be performed).
I am creating some view models for my ASP MVC web app.
I created "code first" models for database. Is it a good way to derive view models from database models?
Example database model:
public class Project
{
[Key]
public int Id { get; set; }
public int? CustomerId { get; set; }
public int TypeId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
View model:
public class ViewModelProject : Project
{
[NotMapped]
public DateTime? Start { get; set; }
[NotMapped]
public DateTime? End { get; set; }
[NotMapped]
public string Manager { get; set; }
}
Is this the right way or is it completely false?
EDIT (subquestion):
I have some very simple database models like ProjectType, which only contains i.e. two properties. Should I also fragment those models in model view or can I make it that way:
Simple database model:
public class ProjectType
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int? Code { get; set; }
}
Can I use it like so:
public class ProjectVM
{
public string Name { get; set; }
public int Number { get; set; }
public ProjectType Type { get; set; }
}
Or does it have to be fragmented like so:
public class ProjectVM
{
public string Name { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int TypeCode { get; set; }
}
I would not recommend doing it this way. I (and many others) have tried it and it doesn't work well. You will inadvertedly run into troubles, since an MVC model has to be tailored to the view and what you get from the DB rarely fits. Sure, you can hammer it into place, but the code quickly gets messy and store-related and UI code starts to mangle together. This even shows in your example, since you have to put the NotMappedAttribute (which is related to data storage), to ViewModelProject (a class at UI level).
There are many other examples to show this problem, but an especially good one I find when you want to serialize a model object to JSON and send it to a JavaScript client. The JSON serializer takes the values of all public properties and adds them to the JSON. If you want to exclude a property, you have to mark it with a ScriptIgnoreAttribute, which you would also have to apply to the base class, which breaks separation between UI and store-related code.
The better way to go is to keep the staorage model and the MVC model separated and to map the data from one to the other (there are already pre-existing frameworks that help you with that, such as Automapper). This comes with additional advantages, for example better testability, since you are now not dependent on a specific data store to create model instances.
I have a bunch of settings that can be one of many types. For example, URL, text, number, etc. Based on other posts I came up with the following that seems to work okay:
public abstract class SettingViewModel
{
public string Name { get; set; }
public string Description { get; set; }
public SettingType Type { get; set; }
public string DefaultValue { get; set; }
public Module? Module { get; set; }
}
public class SettingViewModel<T> : SettingViewModel
{
[Required]
public T Value { get; set; }
}
My biggest problem with this is that when it comes time to read the values in Value for each setting I want to do have a method like. I wouldn't want to change the shape of this method really, because I want to keep it generic and it will be exposed as a Controller Method in MVC
public void Update(SettingViewModel Setting)
{
GS_SysConfig setting = new GS_SysConfig();
//might also just use a util function to convert to string based on the type of Value
setting.ConfigVal = Setting.Value.ToString(; //Value is not a field in the abstract class
db.SaveChanges();
}
This won't work because Value is not a field in the parent class, and I suppose I could come up with some way of doing casting and and getting types and such, but it seems messy and not very elegant. Is there a better approach to tryign to accomplish what I'm trying to do?
Update:
ConfigVal is of type String
Ok, I have 3 models. WorkoutViewModel has a one to many relationship with WorkoutExerciseViewModel. WorkoutExerciseViewModel has a one to many relationship with ExerciseSetViewModel. I need a dynamic “Create View”, that will allow me dynamically add Exercises to Workouts, and Sets to Exercises. I then want to save a Workout including all exercise and set records back to the database. I just need to validate that there is at least 1 exercise for the workout created and at least 1 set for the exercise created. Ultimately I just need to push a Workout View Model back to the controller with all of the populated nested IEnumberable objects present. Can anyone point me in the right direction?
public class WorkoutViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtal IEnumerable<WorkoutExerciseViewModel> WorkoutExercises { get; set;}
}
public class WorkoutExerciseViewModel
{
public int Id { get; set; }
public int WorkoutId { get; set; }
public int ExerciseId { get; set; }
public virtual ExerciseViewModel Exercise { get; set; }
public virtual IEnumerable<ExerciseSetViewModel> ExerciseSets { get; set; }
public string ExerciseFullname
{
get
{
return Exercise.Equipment.Name + " " + Exercise.Name;
}
}
}
public class ExerciseSetViewModel
{
public int Id { get; set; }
public int WorkoutExerciseId { get; set; }
public int Set { get; set; }
public int Reps { get; set; }
public int Weight { get; set; }
public string WeightValueType { get; set; }
}
There's really more to this than can reasonably be discussed in a StackOverflow answer, but I'll give you enough to start with.
As far as adding new exercises and sets within those exercises go, that's just JavaScript. You'll need to have some button that the user can click to add a new one, and tie the click event on that button to a handler that will add the appropriate HTML (form fields and such) to the page. There's many different ways to go about doing that, some more difficult than others. Most likely you want to look into some JavaScript templating library or a more full stack JS library like Knockout to make things easier. The only other thing to keep in mind is the conventions the modelbinder uses to wire everything from the post body to an instance of your model. For collections, it expects fields to have name attributes in the form of CollectionPropertyName[N].PropertyBeingEdited, where N is the position within the collection. So, the name attribute for ExerciseFullName for the first exercise would be WorkoutExercises[0].ExerciseFullName.
Your post action would simply take your same main view model:
[HttpPost]
public ActionResult Create(WorkoutViewModel model)
{
...
}
As long as you follow the property naming conventions for all the fields in your form, the modelbinder will happily wire everything from the post body onto your WorkoutViewModel instance.