Suppose I have a model with 20 fields, and in my index page, I want to list all models that are stored in my database.
In index page, instead of listing all fields of the model, I only to list 3 fields.
So, I make two class:
class CompleteModel {
public int Id { get; set; }
public string Field01 { get; set; }
public string Field02 { get; set; }
public string Field03 { get; set; }
public string Field04 { get; set; }
public string Field05 { get; set; }
...
public string Field20 { get; set; }
}
now, in my Controller, I can use:
await _context.CompleteModel.ToListAsync();
but I feel that it does not seem to be the right way to do it, because I'm getting all fields and using only 3 fields.
So, I made this code:
class ViewModel {
public string Field02 { get; set; }
public string Field04 { get; set; }
public string Field08 { get; set; }
}
var result = _context.CompleteModel.Select(
x => new {
x.Field02,
x.Field04,
x.Field08
}).ToListAsync();
var listResults = new List<IndexViewModel>();
if (result != null)
{
listResults.AddRange(results.Select(x => new IndexViewModel
{
Field02 = x.Field02,
Field04 = x.Field04,
Field08 = x.Field08
}));
}
I think this is a lot of code to do this.
First, I selected all the fields that I want, then, copied everything to another object.
There's a "more directly" way to do the same thing?
Like:
_context.CompleteModel.Select(x => new IndexViewModel { Field02, Field04, Field08 });
You could use AutoMapper to reduce the boiler plate so you're not manually copying field values over.
If you include the AutoMapper NuGet package then you'd need to have the following in your startup somewhere to configure it for your classes:
Mapper.Initialize(cfg => cfg.CreateMap<CompleteModel, ViewModel>());
You could then do something like the following:
var results = await _context.CompleteModel.ToListAsync();
var viewModelResults = results.Select(Mapper.Map<ViewModel>).ToList();
There are a lot of configuration options for the package so do take a look at the documentation to see if it suits your needs and determine the best way to use it if it does.
In my view this is one of the weaknesses of over abstraction and layering. The VM contains the data that is valuable to your application within the context of use (screen, process etc). The data model contains all the data that could be stored that might be relevant. At some point you need to match the two.
Use EF Projection to fetch only the data you need from the database into projected data model classes (using the EF POCO layer to define the query, but not to store the resultant data).
Map the projected classes onto your VM, if there is a naieve mapping, using Automapper or similar. However unless you are just writing CRUD screens a simple field by field mapping is of little value; the data you fetch from your data store via EF is in its raw, probably relational form. The data required by your VM is probably not going to fit that form very neatly (again, unless you are doing a simple CRUD form), so you are going to need to add some value by coding the relationship between the data store and the View Model.
I think concentrating on the count of lines of code would lead to the wrong approach. I think you can look at that code and ask "is it adding any value". If you can delegate the task to Automapper, then great; but your VM isn't really pulling its weight other than adding some validation annotation if you can consistently delegate the task of data model to VM data copying.
Related
I am very new to asp.net and C# so bear with me. I am trying to return data from a database using the entity framework .include() method so that I can get the foreign key information from another table. However, what is being returned is only part of the data. It seems to be cut off before everything is returned.
"[{"id":11,"name":"Mr. Not-so-Nice","heroType":3,"heroTypeNavigation":{"id":3,"type":"Villian","heroes":["
Which gives me the error: SyntaxError: Unexpected end of JSON input.
Please seem below for the model classes and the GET section of the controller where this is being returned. If I remove the "include()" method it returns all the heroes from the main table just fine.
public partial class Hero
{
public int Id { get; set; }
public string Name { get; set; }
public int? HeroType { get; set; }
public virtual HeroTypes HeroTypeNavigation { get; set; }
}
{
public partial class HeroTypes
{
public HeroTypes()
{
Heroes = new HashSet<Hero>();
}
public int Id { get; set; }
public string Type { get; set; }
public virtual ICollection<Hero> Heroes { get; set; }
}
// GET: api/Heroes
[HttpGet]
public async Task<ActionResult<IEnumerable<Hero>>> GetHeroesTable()
{
return await _context.HeroesTable.Include(hero => hero.HeroTypeNavigation).ToListAsync();
}
Serializer recursion rules will be tripping this up. Basically as jonsca mentions, you have a circular reference between hero, and hero type. The serializer will start with the hero, then go to serialize the hero type which it will find the Hero's collection and expect to serialize, which each would reference a hero type, with collections of Heros.. The serializer bails when it sees this.
I would recommend avoiding passing back Entity classes to your view to avoid issues with EF and lazy loading. Serialization will iterate over properties, and this will trigger lazy loads. To avoid this, construct a view model for the details your view needs, flatten as necessary.
For example if you want to display a list of Heroes with their Type:
public class HeroViewModel
{
public int HeroId { get; set; }
public string Name { get; set; }
public string HeroType { get; set; }
}
to load:
var heroes = await _context.HeroesTable.Select(x => new HeroViewModel
{
HeroId = x.HeroId,
Name = x.Name,
HeroType = x.HeroType.Type
}).ToListAsync();
You can utilize Automapper for example to help translate entities to view models without that explicit code using ProjectTo<TEntity> which can work with EF's IQueryable implementation.
With larger realistic domains your client likely won't need everything in the object graph.
You won't expose more information than you need to. (I.e. visible via debugging tools)
You'll get a performance boost from not loading the entire graph or triggering
lazy load calls, and it's less data across the wire.
The last point is a rather important one as with complex object graphs, SQL can do a lot of the lifting resulting in a much more efficient query than loading "everything". Lazy hits to the database can easily add several seconds to each and every call from a client, and loading large graphs has a memory implication on the servers as well.
I made a register page with dynamic form in Orchard CMS, and received new requirements of checking record count.
I have no idea about how to do this, I looked into the SubmissionAdminController.cs in Orchard.DynamicForms.Controllers folder, but still could not find a way.
I'm thinking to get the record count from my cshtml view page and check it in different parts, is it possible?
To get the record count of the stored submissions, inject or resolve an IRepository<Submission>, and use the Count() method to count all items. Note that the Count() method accepts an expression, which allows you to filter by form name for example. For reference, this is what the Submission class looks like:
namespace Orchard.DynamicForms.Models {
public class Submission {
public virtual int Id { get; set; }
public virtual string FormName { get; set; }
[StringLengthMax]
public virtual string FormData { get; set; }
public virtual DateTime CreatedUtc { get; set; }
}
}
When you have an IRepository<Submission>, this is how you would count all submissions in a form called "MyForm":
var count = submissionRepository.Count(x => x.FormName == "MyForm");
If you don't have a controller or custom part or anything to inject this IRepository into, then you could resolve the repository directly form your view like this:
#{
var submissionRepository = WorkContext.Resolve<IRepository<Submission>>();
var submissionCount = submissionRepository.Count(x => x.FormName == "MyForm");
}
Make sure to import the proper namespaces:
Orchard.DynamicForms.Models for Submission
Orchard.Data for IRepository<T>
However, if you need to display this number in multiple places, it's best to create a shape so that you can reuse it. Even better would be to not resolve the repository from the shape template directly, but via an IShapeTableProvider. The primary reason for that becomes clear when you start overriding your shape template or start providing shape alternates, both in which cases you'll have duplicate logic in all of your shape templates, which isn't very DRY of course. And there's the more philosophical issue of separation of concerns: you don't want data access code in your views. Rather, use that code from a controller, driver or shape table provider.
I am currently typing this from my phone, so I don't have any code at hand. But I'll try to explain the situation.
Taking the example of a Recipe and Variations to this Recipe. The Recipe in this case holds a collection of Variations.
I have a Create view that takes the Recipe as the model.
I want this view to contain a list of Variations to the Recipe already. So there is an 'Add variation' button that opens a dialog with a partial view to add this variation.
Given that this is a yet non existing Recipe, I can't save the variation to the database yet, because it requires the RecipeId. So I think I am looking for a temporary place to store the new variations until I am saving the Recipe, and then also save the variations with it.
As MVC is stateless, I can't really save the variations anywhere temporarily, except for possibly the viewdata or tempdata, or I could add a bunch of hidden fields to the page via JavaScript, so that the Variation entries will be taken into the post triggered by saving the Recipe. All of which don't sound particularly tidy solutions.
What would be the most clean, ideal solution here?
A couple of possible solutions:
Reconsider to use a wizard flow for creation instead - create recipe screen (no variations), then a chain of create variation screens. After that when you edit you can do it in the same page since the recipe is saved already.
As dreza mentionned, the variation partial view could return html to the creation page that adds the fields needed in the form for a full submit of the recipe + variations.
Pre-generate the RecipeId on the create form and reference to it for every actions, so you could save Variation (Id, RecipeId, ...) in the database before saving Recipe if you have different table/document. Then you lose the autogenerate id from SQL/Entity tho, and it's harder to maintain.
If you'd use a document database, you could have had the Recipe document have a list of VariationId and create the Variation on it's own without any direct reference to Recipe directly (No RecipeId in Variation). In SQL/Entity tho you would need 3 tables - Recipe (RecipeId), Variation (VariationId), RecipeVariation (RecipeId,VariationId) - it's uglier.
However, with complex, error prone UI like a parent + childs and such, another approach, and one I used extensively, is to create InputContexts associated to users and have InputModels are well as your normal models. These are semi-temporary models that exists until the user finishes to do his entry, they are usually used as ViewModels directly. For example, you could have:
public class InputContext
{
public int Id { get; set; }
public int UserId { get; set; }
public string RouteName { get; set; }
public DateTime TimeStamp { get; set; }
//...
}
public class RecipeInput
{
public int Id { get; set; }
public int ContextId { get; set; }
// ...
}
public class VariationInput
{
public int Id { get; set; }
public int ContextId { get; set; }
// ...
}
Your partial view that creates variation could simply save them to VariationInput with the current ContextId and that's it.
Then in the controller:
[HttpGet]
public ActionResult Create()
{
// We should check if one exists and reuse it instead, but omitted for clarity
var context = new InputContext(GetUserId(), GetRouteName());
db.Contexts.Add(context);
db.Save(context);
var model = new RecipeInput(context.Id);
return View(model);
}
[HttpPost]
public ActionResult Create(RecipeInput model)
{
// Save the current input to DB
db.RecipeInputs.Update(model);
db.Save();
// Do validation and return Create view on error...
// load the context and related variations created
var context = db.Contexts.Find(model.ContextId);
var children = db.VariationInputs.Where(x => x.ContextId == context.Id).ToList();
// Create the actual models from the input.
var recipe = new Recipe();
// set values from model
foreach (var child in children )
{
var variation = new Variation();
// set values from child
recipe.Variations.Add(variation);
}
db.Recipes.Add(recipe);
db.Save();
// Cleanup if it worked
db.RecipeInputs.Delete(model.Id);
foreach (var child in children )
{
db.VariationInputs.Delete(child.Id);
}
// you could keep the Contexts as logs or delete them
//...
}
This is quite more complicated, but it can be used to save user data even between browser restart, session timeouts (they leave for lunch and come back and have to input everything again...), etc, and do some other neat tricks.
I usually store them in another database/partition/schema, and when I use SQL as a backend (because that's what the client wants), I often try use a Document Database (RavenDB or MongoDB for example) to store the context and input models as it is much nicer to play with in terms of MVC. This is not client data so it doesn't matter much.
If you need to see the list of variations in the Create Recipe page, you simply need to do have a partial view/ajax that gets a list of VariationInput in the current ContextId and display these.
In my solution I have two projects.
Project 1 (Core)
Mapping SQL to DTO using Dapper
Project 2 (WebUI - ASP.NET MVC 4)
Here I use a ViewModel per View.
Examples of a Controller
[HttpGet]
public ActionResult Edit(int id)
{
// Get my ProductDto in Core
var product = Using<ProductService>().Single(id);
var vm = new ProductFormModel(product);
return View(vm);
}
Examples of a ViewModel
public class ProductFormModel : BaseViewModel, ICreateProductCommand
{
public int ProductId { get; set; }
public int ProductGroupId { get; set; }
public string ArtNo { get; set; }
public bool IsDefault { get; set; }
public string Description { get; set; }
public string Specification { get; set; }
public string Unit { get; set; }
public string Account { get; set; }
public decimal NetPrice { get; set; }
public ProductFormModel(int productGroupId)
{
this.ProductGroupId = productGroupId;
}
public ProductFormModel(ProductDto dto)
{
this.ProductId = dto.ProductId;
this.ProductGroupId = dto.ProductGroupId;
this.ArtNo = dto.ArtNo;
this.IsDefault = dto.IsDefault;
this.Description = dto.Description;
this.Specification = dto.Specification;
this.Unit = dto.Unit;
this.Account = dto.Account;
this.NetPrice = dto.NetPrice;
}
public ProductFormModel()
{
}
}
Explanation:
I'll get my DTOs in my controller using a service class in the project (Core).
Then i create my ViewModel and pass the DTO to the constructor in ViewModel.
I can also use this view to add a new Product because my ViewModel can take a empty constructor.
Does anyone have experience of this. I wonder if I am in this way will have problems in the future as the project gets bigger?
I know this has nothing to do with Dapper. But I would still like a good way to explain my solution.
I think you will be fine using your current approach. More importantly, start out like this and refactor if you start to encounter problems related to your object mapping code (instead of thinking too much about it beforehand).
Another way to organize mapping logic that I use sometimes is to employ extension methods. That way, the mapping code is kept separate from the view model itself. Something like:
public static class ProductMappingExtensions
{
public static ProductFormModel ToViewModel(this ProductDto dto)
{
// Mapping code goes here
}
}
// Usage:
var viewModel = dto.ToViewModel();
Yet another approach would be to use a mapping framework like AutoMapper - this is a good fit in particular if your mapping logic is simple (lots of 1:1 mappings between properties).
But again, start simple and refactor when you need to.
I realize that this is a little bit late answer, but maybe it will help someone in the future.
This way of doing mapping between objects breaks the 'S' of the SOLID principles, because the responsibility of the ViewModel is to prepare data in its properties to be ready to use by the view and nothing else, therefore, mapping objects should not be on it's responsibilities.
Another drawback of this way is that it also breaks the 'Loose Coupling' OO principle as you ViewModel is strongly coupled with your DTO.
I think, even when we are in the very first step of the project, there are some importants OO principles that we should never break, so using mapper classes, either auto (AutoMapper, ValueInjecter ...) or manual, is definitely better.
I'm still not yet sure on the best way to store selectlist options for front end display or db storage.
I've been using Enums at the moment, and also using description decorators (How do you create a dropdownlist from an enum in ASP.NET MVC?)
I'm now thinking that I might as well just create a full class for this stuff, so I can store the following information properly with full control:
Item Name
Full description
int for storage in db
order
Any methods to get information in anyway from the list.
Is it right I should be thinking about implementing all this myself by hand? I want a really solid way of doing this, and an enum doesn't really feel like it's going to cut it.
Is it right I should be thinking about implementing all this myself by
hand?
Yes. Enums are often leaky and insufficient abstractions that aren't always suitable for the complex domain model you actually wish to represent.
Rather than roll your own, you may want to consider Headspring's Enumeration class (via github, nuget). We use it all the time instead of enums because it's nearly as simple and is much more flexible.
An example of a "State" enumeration and using it as a select list:
public class State : Enumeration<State>
{
public static State Alabama = new State(1, "AL", "Alabama");
public static State Alaska = new State(2, "AK", "Alaska");
// .. many more
public static State Wyoming = new State(3, "WY", "Wyoming");
public State(int value, string displayName, string description) : base(value, displayName)
{
Description = description;
}
public string Description { get; private set; }
}
public IEnumerable<SelectListItem> Creating_a_select_list(State selected)
{
return State.GetAll().Select(
x => new SelectListItem
{
Selected = x == selected,
Text = x.Description,
Value = x.Value.ToString()
});
}
I'm not trying to sell you on this particular implementation, you could certainly hand code your own (the Enumeration class is only about 100 lines of code). But I definitely think you'd benefit from moving beyond basic enums. It is the right approach given the scenario you described in your question.
The first place where such information shoiuld be is the database...or any "virtual store" such as a web service that offers an interface to you db. In fact if there are other db entiies that use these values THEY MUST be represented in the database, otherwise you will run in big troubles. In fact, suppose one of such values is a string....if you don't define a table containing all possible values+a key and simply write the string as it is in other tables...it will be impossible for you to change the format of the string since it will be "spread" all over your db...On the contrary, if you just use an external key to refer to such strings...you can easily change them since the string is stored in just ONE place in your db.
Also the enumeration solution suffers of the problem that you cannot add or deleted values...so if such operations "conceptually" might make sense you cannot use an enumeration. You can use enumeration when all options "conceptually span" all possibilities, so you are sure you will never add/delete other options, such as in the case of the enumeration (yes, no, unknown).
That said, once you have your options in the db the remainder is easy...you will have DTO entities or Business entities representing them in exactly the same way you do for all other DB entities.
For visualization purposes you may have a ViewModel version of this options that might just contain key and description, and a "Repository method" that your controllers can call to have the list of all options.
Once retrieved you controllers put them in the overall page ViewViewModel...together with all other information to be shown on the page. From the ViewModel...you can access them to put them in a dropdown.
Summing up:
1) You need a DB representation of your options
2) Then you will have DTO, business layer, and View versions of this entities...as needed, exactly as for all other DB entities.
Are you looking for a one-size-fits-all solution for all your select list options? I personally advocate choosing the option that best fits the specific issue.
In a recent project I was introduced to a hybrid of a Smart Enum. Here's an example (I apologize for typos, I'm typing this cold):
public class Priority
{
public enum Types
{
High,
Medium,
Low
}
public Types Type { get; private set; }
public string Name { get { return this.Type.ToString(); } } // ToString() with no arguments is not deprecated
public string Description { get; private set; }
public static High = new Priority{ Type = Types.High, Description = "..."};
public static Medium = new Priority{ Type = Types.Medium, Description = "..."};
public static Low = new Priority{ Type = Types.Low, Description = "..."};
public static IEnumerable<Priority> All = new[]{High, Medium, Low};
public static Priority For(Types priorityType)
{
return All.Single(x => x.Type == priorityType);
}
}
So, in implementation, you could store the Enum value, but you would reference the object itself (Priority.For(entity.priority)) for the additional metadata when rendering your views.
Is that closer to what you're looking for?
Of course, one of the gotchas is if you need to write a query against the database that relies on the metadata on the lookup, this solution is going to create a few tears along the way.
You can use "repository pattern" for data access and use viewmodels between your controllers and views. Example:
//Model
public class CustomerViewModel
{
public Customer customer { get;set; }
public IEnumerable<Village> Villages { get; set; }
}
//Controller
public ActionResult Index()
{
var customerViewModel = new CustomerViewModel
{
Customer = new Customer(),
Villages = _villageService.GetAll()
};
return View(customerViewModel);
}
//View
#model ViewModel.RegisterViewModel
#Html.DropDownListFor(q => q.Customer.VillageId, new SelectList(Model.Villages, "Id", "Title"), "Please Select")
I have written a blog post about repository pattern, you may have a look.
I store my options in the View Models themselves:
public class ViewModel {
[Required]
public int SelectListValue { get; set; }
public IDictionary<String,String> SelectListOptions {
get {
return new Dictionary<String, String>{
{ "0", Resources.Option1},
{ "1", Resources.Option2},
{ "2", Resources.Option3}
};
}
}
}
Then I can just drop the following line into my view to render the select list:
<%= Html.DropDownListFor(m => m.SelectListValue, new SelectList(this.Model.SelectListOptions, "Key", "Value", "")) %>