I am trying to develop a simple MVC 2 timesheet application for my small business.
I have a sort of mock model for now until I have a database in place, just to make things simpler while I develop the functionality. It consists of the following:
public class CustomersRepository
{
public CustomersRepository()
{
Customers = new List<Customer>();
}
public List<Customer> Customers { get; set; }
}
public class Task
{
public Task()
{
Customer = new Customer();
TimeSegments = new List<TimeSegment>();
}
public override string ToString()
{
return Name;
}
public string Name { get; set; }
public Customer Customer { get; set; }
public List<TimeSegment> TimeSegments { get; set; }
}
public class TimeSegment
{
public string Id { get; set; }
public string Date { get; set; }
public int Hours { get; set; }
}
public class Customer
{
//To show the name in the combobox instead of the object name.
public override string ToString()
{
return Name;
}
public Customer()
{
Tasks = new List<Task>();
}
public List<Task> Tasks { get; set; }
public string Name { get; set; }
}
I initialize the repository in the controller, and pass the "model" to the view:
CustomersRepository model = new CustomersRepository();
public ActionResult Index()
{
InitializeRepository();
return View(model);
}
Now, in the view I populate a dropdownlist with the customers:
<div>
<%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers), new {#id="customerDropDownList"}) %>
</div>
But then I need to populate a second dropdownlist (taskDropDownList for the tasks associated with a particular customer) based on the selection the user chooses in the customer dropdownlist.
But how do I do this exactly? I have seen examples with jQuery, but I'm not sure how to apply them to this situation. Also, the examples seem to just populate the lists with string values. I need to be able to access the objects with all their properties. Because the next thing I need to do is to be able to populate the TimeSegments list of the selected task with values from input fields (i.e. the hours worked for particular dates). And for that to be saved to the "model" (eventually to the database) in the controller, how do I get it there, unless the objects are all part of the same model bound to the View?
I'm on rather thin ice with this since I still find the connection between the View and the Controller hard to handle, compared with e.g. Windows development, where these things are rather easy to do. So I would really appreciate a good step by step example if anyone would be so kind as to provide that!
I found the answer here:
http://www.pieterg.com/post/2010/04/12/Cascading-DropDownList-with-ASPNET-MVC-and-JQuery.aspx
It needed some tweaks, and I got help here from CGK. See this post:
Cascading dropdownlist with mvc and jQuery not working
Related
I have a simple POCO type, say something like
public class OwnedEntity {
public string stringProperty { get; set; }
public decimal decimalProperty { get; set; }
public bool boolProperty { get; set; }
public int intProperty { get; set; }
}
and an actual entity with an OwnedEntity reference
public class SomeEntity {
public string Id { get; set; }
public OwnedEntity OwnedEntity { get; set; }
}
I set up the relationship like described in the documentation using EF Core's Fluent API:
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<SomeEntity> ().OwnsOne (e => e.OwnedEntity);
}
I can't find anything on how to define default-values for all the properties of OwnedEntity. I tried to initialize the properties like this:
public class OwnedEntity {
public string stringProperty { get; set; } = "initial"
public decimal decimalProperty { get; set; } = -1M;
public bool boolProperty { get; set; } = false;
public int intProperty { get; set; } = -1;
}
but with no effect. Same goes with the [DefaultValueAttribute] (but that was to expect since it's explicitly mentioned).
There's a bit of information on how to handle initial values for regular entities:
modelBuilder.Entity<SomeOtherEntity>()
.Property(e => e.SomeIntProperty)
.HasDefaultValue(3);
But since I'm facing an Owned Entity Type, I can't access the type via Entity<T>.
Is there a way of doing what I'm looking for?
Some things worth mentioning:
I have a solid amount of specific entities where most of them are using the OwnsOne relation
Declaring all OwnedEntity-properties in a base class is not an option since not all the entities have those properties
I`m using EF Core 2.0.3 and ASP.NET Core MVC 2.0.4
Edit:
Originally, I wanted to have newly created SomeEntity instances to come with preset properties for all of the 'embedded' SomeEntity.OwnedEntity properties.
But looking at how my associated controller works, it all makes sense... I have the following methods for the 'Create' operation:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create (SomeEntity model) {
context.Add (model);
await context.SaveChangesAsync ();
// redirect etc.
}
Which means that no object is created for the [HttGet] overload of Create and all the HTML inputs linked to properties (via asp-for) are initially empty. Okay. So I guess the proper way of doing this is to manually create a new instance of SomeEntity and pass it to the Create view like this:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create), new SomeEntity());
}
Is this the right approach then or are there some more things to keep in mind?
Assuming you understand what EF Core Default Values are for, and just looking for equivalent of Entity<T>().Property(...) equivalent.
The owned entities are always configured for each owner type by using the ReferenceOwnershipBuilder<TEntity,TRelatedEntity> class methods. To access this class you either use the result of OwnsOne method, or use the OwnsOne overload taking second argument of type Action<ReferenceOwnershipBuilder<TEntity,TRelatedEntity>>.
For instance, using the second approach:
builder.Entity<SomeEntity>().OwnsOne(e => e.OwnedEntity, ob =>
{
ob.Property(e => e.stringProperty)
.HasDefaultValue("initial");
ob.Property(e => e.decimalProperty)
.HasDefaultValue(-1M);
// etc.
});
I am new to MVC3
DB has a table called users (id,name,username,lat,lon). I do not have access to the database.
I created entity model using the above table. Then, I created a SearchController and Search View. On the index page I display the list of all users. Also, I created a form on the page that searches the table using the username. After clicking on the details link I show the details page where I display the user's details.
Now I need to display other users that are within a mile of the selected user. I already have the sql query to get the list of other users that are within a mile of the selected user (This is a sql query). I need to show this list on Details page.
I created a custom class model.
public class SearchDetailsViewModel
{
public decimal id { get; set; }
public string name { get; set; }
public string username { get; set; }
public Nullable<decimal> lat { get; set; }
public Nullable<decimal> lon { get; set; }
public string profile_img_url { get; set; }
public IQueryable<user> usersWithinAMile { get; set; }
}
I am not sure if this is the right approach. Also, I do not know how I would intialize this class.
Any help or suggestion is really appreciated.
Yes, you are on the right track, you get the data from your SQL into the class, and then to the view, in the ActionResult, like this:
using System.Collections.Generic;
public ActionResult Index() {
var data = new List<SearchDetailsViewModel>();
// foreach row in your SQL
var s = new SearchDetailsViewModel();
s.lat = -42.00000M;
// more data added to the s object here
data.Add(s);
// end loop
return View("ThePage", data);
}
Then on your view itself you would do something like this:
#foreach (var s in Model) {
// output your values on the page
}
I'm having a problem handling multiple values from a strongly typed Listbox.
I have an Event Class that can have multiple Technology classes.
Here's my simplified Event class:
public class Event
{
public int ID { get; set; }
public IEnumerable<Technology> Technologies { get; set; }
}
I was using this
public List<Technology> Technologies { get; set; }
and changed to
public IEnumerable<Technology> Technologies { get; set; }
but still got the same error.
Here's the Technology class, it's a really simple one
public class Technology
{
public int ID { get; set; }
public String Description{ get; set; }
}
here's my simplified Controller
public ActionResult Create()
{
var TechnologyQry = from d in db.Technology
orderby d.Description
select new { d.ID, d.Description };
ViewBag.eventTechnology = new MultiSelectList(TechnologyQry ,"ID","Description");
return View();
}
and here's the view portion that renders the ListBox
#Html.ListBoxFor(model => model.Technologies, (MultiSelectList)ViewBag.eventTechnology)
and thit's the error I'm getting
The ViewData item that has the key 'Technologies' is of type 'System.Collections.Generic.List`1[[stuff.Models.Technology, stuff, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' but must be of type 'IEnumerable'./
Sorry, English is not my main language.
I'd appreciate any help I can get.
Thank you.
What Model have do you have defined in the View #model ...?
Besides that, the Create method isn't returning something to be used for/as the model.
If you want the listbox to be called "Technologies" in the name attribute, you could instead use:
#Html.ListBox("Technologies", (MultiSelectList)ViewBag.eventTechnology)
I'm having some troubles with validation on my application.
Let's say I've the following models:
public class Company
{
public int id { get; set; }
[Required]
public String Name { get; set; }
public String Location { get; set; }
public List<Contacts> Contacts { get; set; }
}
public class Contact
{
public int id { get; set; }
[Required]
public String Name { get; set; }
[DataType(DataType.EmailAddress)]
public String Email { get; set; }
public String Telephone { get; set; }
public String Mobile { get; set; }
}
Now in my company create view I've two buttons, one to add contacts to the company, and another one to create the new company.
I detected which button was used in my controller like this (both buttons are named "button"):
[HttpPost]
public ActionResult Create(String button, FormCollection collection)
{
if(button == "AddContact")
{
AddContact(collection);
}
else
{
CreateCompany(collection);
}
}
While it's being created the object that represents the company that it's being create is stored in the session (for example HttpContext.Session["company"] = company;)
Now the problem is that if, for example, I try to add a contact without first specifying the company name, i get a validation error because the company name is required, which shouldn't happen because the user might want to add the contacts before adding the company info. Or if I try to save the company, I also get a validation error, because usually when saving the "add contact" form is empty, which means that the contact name (which is required as well) was not specified.
What I want to know is that if it's possible to validate the contact properties only when the addContact button is used, and validate the company properties only when the createCompany button is pressed.
For now i only need to do this serve-side, but if anyone has a solution to do this client-side as well i would appreciate the help.
You could trigger your own validation on the individual objects using
Validator.TryValidateObject(Object, ValidationContext, ICollection)
You can provide conditional validation using the Entity Framework by overriding DbEntityValidationResult in the DbContext. When this validation occurs in the DbContext you can access other entities. When validating a contact you can check the company too. For example:
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
ValidateContact(result);
return result;
}
private void ValidateContact(DbEntityValidationResult result)
{
var contact= result.Entry.Entity as Contact;
if (contact!= null && contact.ContactId != 0)
{
// Add validation code here, such as:
if(!string.IsNullOrEmpty(contact.Company.Name){
result.ValidationErrors.Add(
new DbValidationError(
"Contact",
"Company name cannot be null or empty when validating contacts.")
);
}
}
}
See Julia Lerman's Programming Entity Framework: DbContext http://www.amazon.com/Programming-Entity-Framework-Julia-Lerman/dp/1449312969 for more details.
I am new to MVC. I am developing an web application in asp.net MVC. I have a form through which users can get registered, after registration user redirected to ProfileView.aspx page. till here everything is fine.
Now I want to show the articles headings posted by that user right under his profile.
Right now I m using following code:
public ActionResult ProfileView(int id)
{
Profile profile = profileRepository.GetProfileByID(id);
var articles = articlesRepository.FindArticlesByUserID(id).ToList();
return View("ProfileView", profile);
}
Thanks for helping in advance
Baljit Grewal
I can think of two options:
Use the ViewData dictionary to store the articles.
public ActionResult ProfileView(int id)
{
Profile profile = profileRepository.GetProfileByID(id);
var articles = articlesRepository.FindArticlesByUserID(id).ToList();
ViewData["Articles"] = articles;
return View("ProfileView", profile);
}
Or, if you want to avoid using ViewData, create a ViewModel. A ViewModel is kind of a data transport object. You could create a ProfileViewModel class like this:
public class ProfileViewModel
{
public Profile Profile{ get; set; }
public IList<Article> Articles { get; set; }
}
or just include the Profile properties you are using in your view, this will make binding easier but you'll have to copy values from your Model to your ViewModel in your controller.:
public class ProfileViewModel
{
public int Id{ get; set; }
public string Name { get; set; }
.......
public IList<Article> Articles { get; set; }
}
If you go for this last option take a look at AutoMapper (an object to object mapper).
you will want your page to inherit from ViewPage and then you can use your model inside the .aspx markup like