I am so puzzled with this:
I have two identical set of code for retrieving data from database. One returns data and the other does not, even though data is expected.
The following is the one that returns empty set. There are sample data in the corresponding table.
This is the controller code:
namespace MVCPart1_5.Controllers
{
public class DepartmentController : Controller
{
// GET: Department
public ActionResult Index()
{
DepartmentContext oDepartmentContext = new DepartmentContext();
List<Department> oDepartments = oDepartmentContext.Departments.ToList();
return View(oDepartments);
}
}
}
These are the models:
namespace MVCPart1_5.Models
{
[Table("Department")]
public class Department
{
[Key]
public int Code { get; set; }
public string Name { get; set; }
}
}
namespace MVCPart1_5.Models
{
public class DepartmentContext : DbContext
{
public DbSet<Department> Departments { set; get; }
}
}
and finally the view:
#model IEnumerable<MVCPart1_5.Models.Department>
#using MVCPart1_5.Models;
#{
ViewBag.Title = "Index";
}
<h2>Department Set</h2>
<div style="font-family :Arial">
<ul>
#foreach (Department department in #Model)
{
<li>
#Html.ActionLink(department.Name, "Details", "Employee", new { departmentCode = department.Code })
</li>
}
</ul>
</div>
I also have the following in the global:
protected void Application_Start()
{
Database.SetInitializer<MVCPart1_5.Models.EmployeeContext>(null);
Database.SetInitializer<MVCPart1_5.Models.DepartmentContext>(null);
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
Thanks in advance for your help.
If your oDepartments variable is not filled with the data you should examine a connection string in your application because it's probably wrong (so it connects to another database).
Because your DbContext class is DepartmentContext and the default Entity Framework approach is to name connection string the same you should look for a connection string named DepartmentContext. Then try to connect to a database using credentials found in that connection string. I'm 99% sure you'll find empty Department table.
Remember that even if you connect with the same database server you could connect to wrong database.
Related
I have a navigation bar, with several links, like this:
MenuItem1
This request would hit my action method:
public ActionResult Browse(int departmentId)
{
var complexVM = MyCache.GetComplexVM(departmentId);
return View(complexVM);
}
This is my ComplexVM:
public class ComplexVM
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
}
MyCache, is a static list of departments, which I am keeping in memory, so when user passes in DepartmentId, I wouldn't need to get the corresponding DepartmentName from DB.
This is working fine... but it would be nice if I could somehow initialize ComplexVM in custom model binder, instead of initializing it in the Controller... so I still want to use a link (menu item), but this time, a CustomModelBinder binds my parameter, 2, to ComplexVM: it needs to look up the name of department with id = 2 from MyCache and initialize ComplexVM, then ComplexVM would be passed to this action method:
public ActionResult Browse(ComplexVM complexVM)
{
return View(complexVM);
}
I want to hit the above controller without doing a post-back, as I have a lot of menu item links in my navigation bar... not sure if this is possible? Or if this is even a good idea?
I have seen this link, which sort of describes what I want... but I am not sure how the routing would work... i.e. routing id:2 => ComplexVM
Alternatively would it be possible to do this in RouteConfig, something like this:
routes.MapRoute(
name: "Browse",
url: "{controller}/Browse/{departmentId}",
// this does not compile, just want to explain what I want...
defaults: new { action = "Browse", new ComplexVM(departmentId) });
I can achieve this with little change and with one trick
MenuItem1
Controller action
public ActionResult Browse(ComplexVM complexVM)
{
return View(complexVM);
}
View model
public class ComplexVM
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public ComplexVM()
{
this.DepartmentId = System.Convert.ToInt32(HttpContext.Current.Request("id").ToString);
this.DepartmentName = "Your name from cache"; // Get name from your cache
}
}
This is without using model binder. Trick may help.
That is possible. It is also a good idea :) Off-loading parts of the shared responsibility to models / action filters is great. The only problem is because they are using some special classes to inherit from, testing them sometimes might be slightly harder then just testing the controller. Once you get the hang of it - it's better.
Your complex model should look like
// Your model class
[ModelBinder(typeof(ComplexVMModelBinder)]
public class ComplexVMModel
{
[Required]
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
}
// Your binder class
public class ComplexVMModelBinder : IModelBinder
{
// Returns false if you can't bind.
public bool BindModel(HttpActionContext actionContext, ModelBindingContext modelContext)
{
if (modelContext.ModelType != typeof(ComplexVMModel))
{
return false;
}
// Somehow get the depid from the request - this might not work.
int depId = HttpContext.Current.Request.Params["DepID"];
// Create and assign the model.
bindingContext.Model = new ComplexVMModel() { DepartmentName = CacheLookup(), DepId = depId };
return true;
}
}
Then at the beginning of your action method, you check the ModelState to see if it's valid or not. There are a few things which can make the model state non-valid (like not having a [Required] parameter.)
public ActionResult Browse(ComplexVM complexVM)
{
if (!ModelState.IsValid)
{
//If not valid - return some error view.
}
}
Now you just need to register this Model Binder.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ModelBinders.Binders.Add(typeof(ComplexVMModel), new ComplexVMModelBinder());
}
Your should be able to use the route config that you've provided.
I got the following issue. I'm trying do query my database which was created with code first approach and display the query results in my view.
This is my model class :
public class AddApartmentViewModel
{
public string City { get; set; }
}
public class SearchViewModel
{
public string Query { get; set; }
}
This is my search controller
public class SearchController : Controller
{
private ApartmentContext db = new ApartmentContext();
[HttpPost]
public ActionResult Search(SearchViewModel model)
{
var results = db.Apartments.ToList();
return View(results);
}
}
and this is my search bar which I'm trying to use as a partial view
#using (Html.BeginForm("Search", "SearchController"))
{
#Html.TextBox("Query")
<input type="submit" value="Search">
}
The whole idea is to display search results like in this example
http://www.zoopla.co.uk/to-rent/property/aberdeen/
How to manage this?
I strongly recommend reading this if You want to get listView with search functionality using entity framework.
http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
I am in need of how the correct way to do this.
I can not use forms authentication
A user will "login" or confirm identity based on a value
I need to walk the user through a series of pages like so
Contact/MailAddress
Contact/Phone
Contact/Email
Contact/Summary
Questionaire/Question1
Questionaire/Question2
Questionaire/Question3
Questionaire/Summary
Final/Certify
Final/Review
I plan on using Session to hold the data but I'm having trouble figuring out how to pass the values to other views and how Redirect to other pages.
Any help will do...
Lets say you have some models like this
public class ContactModel
{
public string MailAddress { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
}
public class QuestionaireModel
{
public string Question1Answer { get; set; }
public string Question2Answer { get; set; }
public string Question3Answer { get; set; }
}
public class ContactQuestionaireModel
{
public ContactModel Contact { get; set; }
public QuestionaireModel Question { get; set; }
}
and you want to persist this model from view to view and action to action. In you controller you can create 2 actions. one for your first view and one for your second
Controller
public ActionResult ContactAddress()
{
var model = new ContactQuestionaireModel();
return View(model);
}
[HttpPost]
public ActionResult ContactAddress(ContactQuestionaireModel model)
{
var currentModel = TempData["currentModel"] as ContactQuestionaireModel;
currentModel.Contact.MailAddress = model.Contact.MailAddress;
TempData["currentModel"] = currentModel;
return RedirectToAction("ContactPhone");
}
public ActionResult ContactPhone()
{
var model = TempData["currentModel"] as ContactQuestionaireModel;
return View(model);
}
[HttpPost]
public ActionResult ContactPhone(ContactQuestionaireModel model)
{
var currentModel = TempData["currentModel"] as ContactQuestionaireModel;
currentModel.Contact.Phone = model.Contact.Phone;
TempData["currentModel"] = currentModel;
return RedirectToAction("ContactEmail");
}
in the first action ContactAddress you create a new blank model and pass that in to your view ContactAddress. In that view you can set TempData["currentModel"] equal to the model you are passing in. This will stay in TempData for 1 post back to the server. You dont need to do this on the first page since it's blank anyway but i'm doing it to save time.
View ContactAddress
#model WebApplication3.Models.ContactQuestionaireModel
#{
ViewBag.Title = "Contact Address";
TempData["currentModel"] = Model; //this will be available to me in the HttpPost action
}
#using (Html.BeginForm())
{
<div class="form-group">
#Html.LabelFor(m => m.Contact.MailAddress, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Contact.MailAddress, new { #class = "form-control" })
</div>
</div>
<button type="submit">Submit</button>
}
you'll notice in the controller code that the Post Action for ContactAddress is setting a var currentModel equal to what is in TempData["currentModel"] which was set in the ContactAddress view. Before you do a redirect to the next action ContactPhone set TempData["currentModel"] back to the model you are building and use it in the next action.
You do have the option of adding the Model as a parameter to each action and passing the currentModel object like
public ActionResult ContactPhone(ContactQuestionaireModel model)
return RedirectToAction("ContactPhone", currentModel);
its up to you really. this is not a foolproof way. page refreshes and back and forward buttons could clear out everything that was entered. Storing the information in Session or actually saving the data in a database might be more optimal.
I advise against doing what you are attempting to do by logging in with session but what you are looking for is:
TempData.yourModel = new SomeModel { Data = "yourData" };
//next page
var model = (SomeModel)TempData.yourModel;
and
RedirectToAction("yourController", "yourAction");
I am completely new to MVC and EF ( coming from web forms ) so I apologize up front if this question is dumb ;)
I need to build an app that a user logs into using AspIdentity that also ties to a Community table. Once logged in they can only see data that is community specific site wide. I have the app built but I am struggling with how it will only pull data based on a Community ID... In web forms I would build all this logic myself :/
Basically there are the built in Identity tables and the ones I defined ( Community, Homes, Residents etc. )
Seems like a simple app and it is but I dont see any tutorials that do this.
Thanks!
You can approach it from two ways. If the user only ever belongs to one community, then you can simply use the user's community to restrict your queries. Essentially, you'll do something like:
var foo = db.Foos.SingleOrDefault(m => m.Id == id && m.CommunityId = user.CommunityId);
if (foo == null)
{
return new HttpNotFoundResponse();
}
In other words, you just add an additional condition when pulling items from your database to match the user's community. That way, the user will only ever see something other than a 404 if it belongs to their community.
Option 2 is to make the community part of the URL, and use that to restrict your queries similar to the above. For example, you might have something like /awesome-community/foo. Then, in the action that serves that you'd have something like:
public ActionResult Foo(string community)
{
...
And, again, you'd just limit the query by that community as above. If you follow this approach, though, you will probably then need to also do something like create a custom Authorize attribute that verifies that your user actually belongs to the community.
Here's how I would tackle it, and see if it at least gets you rolling. I'm taking a clean MVC web project that comes with Visual Studio:
I'll also be borrowing the community concept from StackOverflow where you have types of visitors, and content that is applicable. (In the example's case, "Programmers" have their set of questions, while "Designers" have their set.)
An finally, for brevity, I've tried to exclude portion of the files I'm not modifying. To keep things short, existing code has been replaced with a Snip comment.
Onward!
Infrastructure
Setup the additional entities that give us a Community and data specific to that community (Question).
public class Community
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Question
{
public int Id { get; set; }
public int CommunityId { get; set; }
public virtual Community Community { get; set; }
public string Title { get; set; }
}
Add these entities to the ApplicationUser as well as to the ApplicationDbContext.
~/Models/IdentityModels.cs:
public class ApplicationUser : IdentityUser
{
public int CommunityId { get; set; }
public virtual Community Community { get; set; }
/* Snip */
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Community> Communities { get; set; }
public DbSet<Question> Questions { get; set; }
/* Snip */
}
Attach the community to the user. Claims make this really easy since we're just attaching additional information to the user profile. Furthermore, the Visual Studio folks already created a placeholder for us.
// This makes it easier to reference and makes sure we're consistent
public static class CustomClaimTypes
{
public const string CommunityId = "http://stackoverflow.com/claims/communityid";
}
public class ApplicationUser : IdentityUser
{
public int CommunityId { get; set; }
public virtual Community Community { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim(CustomClaimTypes.CommnunityId, this.CommunityId.ToString()));
return userIdentity;
}
}
Note that we've added userIdentity.AddClaim(...) (and our static class).
Presentation
Modify the view model to accommodate our change.
~/Models/AccountViewModels.cs:
public class RegisterViewModel
{
/* snip */
[Required]
[Display(Name = "Community")]
public int CommunityId { get; set; }
}
Plumb this into our Controller.
~/Controllers/AccountController.cs:
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.Communities = GetCommunities();
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
/* Snip */
}
ViewBag.Communities = GetCommunities();
return View(model);
}
// The additional method we're calling above:
private IEnumerable<SelectListItem> GetCommunities()
{
var db = ApplicationDbContext.Create();
var communities = db.Communities.ToDictionary(k => k.Id, v => v.Name);
return communities.Select(c => new SelectListItem
{
Text = c.Value,
Value = c.Key.ToString()
});
}
Flush it out through our view.
~/Views/Account/Register.cshtml:
#* Snip *#
<div class="form-group">
#Html.LabelFor(m => m.CommunityId, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.CommunityId, (IEnumerable<SelectListItem>)ViewBag.Communities, new { #class = "form-control" })
</div>
</div>
#* Snip *#
Create a new action the Home controller:
[Authorize]
public ActionResult Questions()
{
var communityId = GetCommunityId();
var db = ApplicationDbContext.Create();
var community = db.Communities.SingleOrDefault(c => c.Id == communityId);
var questions = db.Questions.Where(c => c.CommunityId == communityId).AsEnumerable();
ViewBag.Message = community.Name;
return View(questions);
}
// Helper that's probably better suited in a more common location
private Int32 GetCommunityId()
{
var communityIdClaim = ((ClaimsPrincipal)User).Claims
.FirstOrDefault(c => c.Type == CustomClaimTypes.CommnunityId);
Int32 communityId;
return communityIdClaim != null && Int32.TryParse(communityIdClaim.Value, out communityId)
? communityId
: -1; // Erroneous id
}
And a view to go with it:
#model IEnumerable<#* Your.Model.Namespace. *#Question>
#{
ViewBag.Title = "Questions";
}
<h2>#ViewBag.Title</h2>
<h3>#ViewBag.Message</h3>
<table class="table">
<thead>
<tr>
<td>Question</td>
</tr>
</thead>
<tbody>
#foreach (var question in Model)
{
<tr>
<td>#question.Title</td>
</tr>
}
</tbody>
</table>
Add a link to your menu.
`~/Views/Shared/_Layout.cshtml
#* Snip *#
<ul class="nav navbar-nav">
#* Snip *#
<li>#Html.ActionLink("Questions", "Questions", "Home")</li>
</ul>
#* Snip *#
Sample Content
Create a very basic DbInitializer that will populate some info we can use. So, back to the ~/ModelsIdentityModels.cs file:
public class ApplicationDbInitializer : CreateDatabaseIfNotExists<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
var communities = new[]{
new Community { Name = "Programmers" },
new Community { Name = "Designers" }
};
var questions = new[]{
new Question { Community = communities[0], Title = "How do I write my first application?" },
new Question { Community = communities[0], Title = "What is unit testing?" },
new Question { Community = communities[0], Title = "Inversion of Control: What is it?" },
new Question { Community = communities[1], Title = "What is a StyleSheet?" },
new Question { Community = communities[1], Title = "HTML5: What do I need to know?" },
new Question { Community = communities[1], Title = "Is Silverlight still being used?" },
};
context.Communities.AddRange(communities);
context.Questions.AddRange(questions);
context.SaveChanges();
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
// for simplicity, let's add a static ctor within the context
static ApplicationDbContext()
{
Database.SetInitializer(new ApplicationDbInitializer());
}
/* Snip */
}
Give it a run-through
Now you should be able to register a user, and see content that's applicable to the community they're a part of:
Paul (the Programmer)
Debby (the Designer)
I know there are a lot of similar question here but none seem quite the same as mine.
In my View:
#model LocalInformedMotionServer.Models.FeedData
#Html.DropDownList("Premise", Model.LoadUp("key"), new { #style = "width: 218px;height:35px;" })
In my controller:
public class CloudController : Controller
{
public IEnumerable<wsCommon.Premise> Premises { get; set; }
public ActionResult Feed(string key)
{
var feedData = new FeedData();
Premises= feedData.LoadUp(key);
return View(feedData);
}
}
In my Model:
public class FeedData
{
public IEnumerable<wsCommon.Premise> LoadUp(string saltKey)
{
Premises premises = new InformedBiz.Premises();
return premises.GetPremises(saltKey);
}
}
It errors because the variable:
"key"
in this call:
Model.LoadUp("key")
is being read in as'null' in my controller method.
Of course as this is all new to me I could be doing this all wrong..
ADDITIONAL:
In my CloudController Class I have this:
public class CloudController : Controller
{
public ActionResult Feed(string saltKey)
{
var feedData = new FeedData();
feedData.LoadUp(saltKey);
return View(feedData);
}
public ActionResult Index()
{
return View();
}
public ActionResult LogIn()
{
return View();
}
}
I'm not sure what your Premise class looks like, but I usually use an IEnumberable of SelectListItem for drop downs in my views. So you could do something like this:
public IEnumerable<SelectListItem> LoadUp(string saltKey)
{
Premises premises = new InformedBiz.Premises();
return premises.GetPremises(saltKey).Select(
p => new SelectListItem { Text = p.Name, Value = z.PremiseId.ToString() }
);
}
You'll also need to create a Post ActionResult method that accepts the model in your view (FeedData) as well as wrap your DropDownList control in a Html.BeginForm, to post results to the controller. Hope this makes a bit of sense.
You have not posted the properties of your FeedData model but assuming it contains a property which is typeof Premise and you want to be able to select a Premise from a collection, then using a view model that represents what you want to display/edit is the recommended approach (refer View Model Design And Use In Razor Views and What is ViewModel in MVC?)
You view model might look like
public class FeedDataVM
{
.....
[Display(Name = "Premise")]
[Required(ErrorMessage = "Please select a Premise")]
public int? SelectedPremise { get; set; }
....
public SelectList PremiseList { get; set; }
}
and in your controller (not sure what saltKey is for?)
public ActionResult Feed(string saltKey)
{
FeedDataVM model = new FeedDataVM();
IEnumerable<Premise> premises = // get the collection of premise objects from your repository
// assuming you want to display the name property of premise, but post back the key property
model.PremiseList = new SelectList(premises, "key", "name");
return View(model);
}
View
#model FeedDataVM
#using(Html.BeginForm())
{
....
#Html.LabelFor(m => m.SelectedPremise)
#Html.DropDownListFor(m => m.SelectedPremise, Model.PremiseList, "-Please select")
#Html.ValidationMessageFor(m => m.SelectedPremise)
....
<input type="submit" value="Save" />
}
and the POST method
public ActionResult Feed(FeedDataVM model)
{
// model.SelectedPremise contains the value of the selected option as defined by the key property of Premise
}
Side note: Your FeedData model contains a method to retrieve a collection of Premise which appears to be calling another service. You should avoid this type of design which makes it difficult to debug and unit test. Your controller is responsible for initializing/getting you data models and view models and for populating/mapping their properties.