I've already read several SO questions about this topic, but honestly most of them have been way too complex for me. I'm very new to ASP.NET mvc.
I have a sample ASP.NET mvc 4 app that I created by following along with (and deviating just a bit from) the Movie database tutorial. It has the built-in account bits, the Entity Framework (which has turned out to be a pain any time I change anything) plus 2 models that I built myself based on the models from the tutorial:
1) Bug
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace MasterDetailPractice.Models
{
public class Bug
{
public int BugID { get; set; }
public string BugTitle { get; set; }
public DateTime BugDate { get; set; }
public string BugStatus { get; set; }
[Column(TypeName = "ntext")]
[MaxLength]
public string BugDescription { get; set; }
}
public class BugDBContext : DbContext
{
public DbSet<Bug> Bugs { get; set; }
public DbSet<Comment> Comments { get; set; }
}
}
2) Comment
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace MasterDetailPractice.Models
{
public class Comment
{
public int CommentID { get; set; }
public int BugID { get; set; }
public int UserID { get; set; }
public DateTime CommentDate { get; set; }
[Column(TypeName = "ntext")]
[MaxLength]
public string CommentText { get; set; }
}
}
When I run my app I'm able to go to /Project and get the standard Index view with the Add link, where I can add a Bug. Once added, I see the usual Edit/Details/Delete links.
When I run my app I'm also able to go to /Comment and get the standard Index view with the Add link, where I can add a Comment. Once added, I see the usual Edit/Details/Delete links.
Up to this point, I'm OK. The CRUD forms work, they just don't work together.
THE PROBLEM:
Currently, in order to make a Comment apply to a Bug, I have to actually input a BugID into the /Comment/Create form. And then the Comments are all only available at the /Comment/ route.
Instead, I need the following to happen:
The "Add Comment" form should automatically know what BugID to
save without a user having to input it.
A master-detail presentation of the data: The /Comment/Index view should appear at the bottom of the /Bug/Edit and/or Bug/Details page and show only the Comments related to the current Bug.
The "Add Comment" link should only appear from the /Bug/Edit or
/Bug/Details page, so Comments are never added without relating to a Bug.
It's kind of amazing that I haven't been able to figure this out myself, after spending 3 days poring over every Google result and SO post I can find on the topic. That said, here I am, hoping to learn the simplest possible implementation of this.
Do I need to post more code (the Controllers, for example, or the Views) in order for this question to be properly answerable?
Looking forward to getting the slow-learning train to start pulling out of the station...
Okay you need to do a few things.
First, create a new action method in your CommentController that looks like this.
public ActionResult Index(int bugId)
{
// Your logic to fetch all comments by BugID through EntityFramework or whatever
return View(data);
}
Now, in your Bug/Edit.cshtml or Bug/Details.cshtml pages add the following line to render those actions inline.
#Html.RenderAction("Index", "Comment", new { #bugId = Model.BugID }
In this case, you should be returning a BugModel back to your Bug/Edit.cshtml or Bug/Details.cshtml anyway as your model.
This should show you the form you need, with the BugID from the model being passed through.
For your last question, just put the "Add Comment" link within your Comment/Index.cshtml view since it will only appear anyway within the context of a bug. You will probably need to wrap this around a form that posts to your CommentController.
Here's a helpful link on working with forms in ASP.NET 4.
http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-helpers,-forms-and-validation
Related
I have a model with a one-to-many relationship kinda similar to this:
public class people
{
public int id { get; set; }
public sting name { get; set; }
public virtual stuff things { get; set; }
}
public class stuff
{
public int id { get; set; }
public string name { get; set; }
public int thingType { get; set; }
}
I now need to be able to create and edit "stuff" for the person I am currently editing on my "editPeople" page. I'm using AJAX to create JQuery UI Dialog box for the stuff edit/create form, and then I post it back to the server to create a bunch of hidden fields so I can actually save this data later.
I'm concerned that these hidden fields would be named in a way that such that when I post my peopleEdit back to the controller, the Model binding won't properly convert the "stuff" that I have created/edited. Am I doing this wrong/Is there a better method? Am I forced into creating a custom model binder?
In my aproach would be if a people can have many stuff and you want to use ajax you can create a partial view to edit the stuff you would send the stuff object from people to the patial view action and run partial view action on the ajax call.
You may consider a javascript framework like Angular.js or Knockout.js to handle the data binding on the client. Then on submit, you can attach a function and the framework would provide you with the values on the model which you can submit to your server via AJAX.
I have been scouring forums and repository pattern blogs for some clear direction on how I should be coding my project and I'm stuck. Any help or guidance from you guys would be much approciated :)
I started my project as EF5 MVC4 Razor Code First and decided to use MVCScaffolding to generate all my controllers, views and repositories. This was my first project with these technologies, I was just told that this was how the team was doing it now (but the previous developers did model first and hand coded their contexts).
SO, all is great, we're coding a bunch of screens, but one of our screens is a complex one that involves many models/sub modlels (ie/ Object model has FKs to Responses, Attachments, Reviewers, etc...). The user adds a bunch of data, selects one or more reviewers, adds 0 or more attachments. Then they hit Save!
Now my big problem is that I want to save all this data as one transaction, and if something fails on one of the children models (ie/ attachments) the transaction will roll back. However, the way the MVCScaffolding repositories are created, each model has it's own instance of DB Context and it's own Save. And the controllers accept each unique repository as parameters for loading the screen data. Another thing to note is for this screen we are using a ViewModel to load the data, and then wrote custom mappers to map back to the different models for saving. We can save each piece separately, and possibly the solution is just to wrap TransactionScope around my save, but I also want to reduce the number of calls to the db, as each repository save does a call.
I thought I could add code to the parent repository for a UnitsOfWork type save that would add/edit all the child obejcts in one context object, but that seems like a hack more than anything, and I want to code this properly.
One of the other projects here just made a custom DB context and all Save methods were in that class, is that the best way to do it? Another dev did code first but hand coded all his Save methods. None of them are in a standard place and he is using TransactionScope with a DBContext inside (is that overkill or does DBContext not handle transactions)?
Since I'm so new to this, I need help and no one I work with seems to agree on a proper method. I'm not sure if my model is wrong for an "MVC App" since I'm such a database heavy thinker.
Below is a sample of my models, any guidance is appreciated. Thanks :)
public class Anomaly
{
[Key]
public int AnomalyId { get; set; }
public string Subject { get; set; }
public int PlantId { get; set; }
[ForeignKey("PlantId")]
public virtual Plant Plant { get; set; }
public DateTime? ReviewByDate { get; set; }
public virtual ICollection<AnomalyReviewer> AnomolyReviewers { get; set; }
public virtual ICollection<AnomalyAttachment> AnomalyAttachments { get; set; }
}
public class AnomalyAttachment
{
[Key]
public int AnomalyAttachmentId { get; set; }
public int AnomalyId { get; set; }
[ForeignKey("AnomalyId")]
public virtual Anomaly Anomaly { get; set; }
public string FilePath { get; set; }
public string FileName { get; set; }
public string FileExtension { get; set; }
public string Notes { get; set; }
}
ps. that is just a sample... thanks!
Just create a class 'Master' that inherits from Controller.
Then write all your queries there as in public User GetUserById(Int32 id)
Finally create a function that does a call to the private!! implementation of DbContext.
I would usually give that function a enum of SystemEvents so i've got a reference of whats happening if something would fail... of course you would need to write your own notificator or model to record your own errors into the database for personal testing...
ive done all this because I can write all my code and found out that Repository Pattern is overkill most of the time if you actually think about it.
I'm learning asp.net mvc3 from w3schools and following that tutorial.http://w3schools.com/aspnet/mvc_models.asp In the section "ASP.NET MVC Models" I have created the model like this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace MvcDemo.Models
{
public class MovieDB
{
public int ID { get; set; }
public string Title { get; set; }
public string Director { get; set; }
public DateTime Date { get; set; }
}
public class MovieDBContext : DbContext
{
public DbSet<MovieDB> Movies { get; set; }
}
}
Then I was going to add a controller according to the instructions.
In the Solution Explorer, right-click the Controllers folder, and select Add and Controller
Set controller name to MoviesController
Select template: Controller with read/write actions and views, using Entity Framework
Select model class: MovieDB (McvDemo.Models)
Select data context class: MovieDBContext (McvDemo.Models)*
Select views Razor (CSHTML)
Click Add
But the problem I have is that the drop down list doesn't show MovieDB (McvDemo.Models) in Model Class and Data Context Class to be selected. Can anyone please help me? Thanks.
You should just be able to recompile (Shift-Ctrl-B) and then try it again - it will be there. Otherwise you can always just declare it yourself at the top of a blank view, but that will not provide the scaffolding that the generator does:
#model MvcDemo.Models.MovieDB;
I recompiled but that did not fix the issue for me and yes I am doing the same thing and ran into the same exact issue. The problem for me was caused by visual web developer not being able to connect to my Movies database. I had to change the definition of my connectionString within web.config like this:
<add name="MovieDBContext"connectionString="Data Source=c:\sites\w3schools_demo\MvcDemo2\MvcDemo2\App_Data\Movies.sdf" providerName="System.Data.SqlServerCe.4.0"/>
If you are having this issue you will need to change the "Data Source" path to point to your Movies.sdf database file.
I have a model that looks like this:
public class Book
{
public string Name { get; set; }
public IEnumerable<Author> Authors { get; set; }
}
public class Author
{
public string FullName { get; set; }
public DateTime BirthDate { get; set; }
}
I have a form in a view for editing a book. The section for editing the Authors collection is in a partial view. The form fields are generated with the Html.EditorFor() method.
It works well for editing existing data. What I would like to do is to put in the Authors editing partial view multiple blank entries that if the user fills them they will be added as new items to the Authors collection.
The final view should look something like this:
http://s1.postimage.org/6g9rqfp20/image.jpg
What is the correct way to achieve this behavior?
If you are using MVC2 this is your best bet
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx#related-results
I am not sure how interested you are in using javascript libraries to get what you are looking to get done, but here is a great example of what you are trying to do: Contact Editor Example
It uses the knockouts library which allows you to work with JavaScript data binding. This also gives you a nice thick application feel on the web which users generally like.
If you are still curious about how this works with serverside, you can look at this presentation from Mix11
Good luck.
Going mad now. I have a MVC solution that i've upgraded from MVC 1 to 2. It all works fine.... except the Validation!
Here's some code:
In the controller:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Web.UI;
using MF.Services.Authentication;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MF.Controllers
{
//basic viewmodel
public class LogOnViewData
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
[HandleError]
public class AccountController : Controller
{
[HttpPost]
public ActionResult LogOn(LogOnViewData lvd, string returnUrl)
{
if (ModelState.IsValid)
{
//do stuff - IsValid is always true
}
}
}
}
The ModelState is always valid. The model is being populated correctly however. Therefore, if I leave both username and password blank, and post the form the model state is still valid. Argh!
Extra info: using structure map for IoD. Previously, before upgrading to MVC 2 was using the MS data annotation library so had this in my global.asax.cs:
ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder();
Have removed that now.
I'm sure i'm doing something really basic and wrong. If someone could point it out that would be marvellous.
Cheers
Half way through the development of MVC2, they went from input validation to model validation, which should in all cases validate your object completely. Make sure you're using the latest version (RTM).
However, [Required] merely indicates the attribute must not be null. Unfortunately, String.Empty -which is the default for strings- is not null, so model validation will pass for empty strings.
See this post by Brad Wilson for important details.
As a solution, you could use the [RegularExpression("....")] to impose restrictions on the minimum string length and allowed characters.