I'm using code first feature of EntityFramework and I have created a model class with this field that have their own property (I don't mention them here!) :
public class Portrait
{
private Guid _id;
private string _aboutimage;
private string _aboutMe;
private string _mainMenu;
private string _headerImage;
private string _resume;
private string _showpiece;
private string _siteMenu;
private string _adminMenu;
}
and for each part of this class I have separate ViewModel, for example I have AboutViewModel to update About in admin part and navigate in about page in website :
public class AboutViewModel
{
public Guid Id { get; set; }
public string AboutText { get; set; }
public string Image { get; set; }
}
Now when I update the AboutViewModel, Portrait table in database will create a record that will have about text (other field will be null)
And for updating for other part of this table like Resume or others, it will generate another record with updated and inserted field (now in this record about text will be null!)
Now how can I get the about field to show it in UI as I have several records !? I don't want and also can not get these field by ID because I always want to have their latest updated to show in web site, what I have written is like this ,my about action to get the about text is like this :
public ViewResult About()
{
var about= _portraitRepository.GetContent();
return View(about);
}
and the GetContent() is like this :
public Portrait GetContent()
{
return _siteContext.Portraits.Find();
}
but dosn't work and I got this error:
The number of primary key values passed must match number of primary key values defined on the entity. Parameter name: keyValues
Am I in a wrong direction? How can I solve this problem please?
Another option is to get the Max value of the Id of Portrait table if it is an identity column.
The action will look like below.
public ActionResult About()
{
var about= _portraitRepository.GetLatest();
}
Repository will look like below.
public Portrait GetLatest()
{
var latestId = _siteContext.Portraits.Max(p => p.Id);
return _siteContext.Portraits.Find(latestId);
}
You should retrieve the ID when you perform the insert. Then save that value to Session or something for later use in your About Action
The call to the repository to persom the insert will look like below.
Session["LatestPortraitId"] = _portraitRepository.AddPortrait();
The method in the Portrait repository used to insert a new Portrait should look like below.
public int AddPortrait(portrait)
{
_siteContext.AddObject(portrait);
_siteContext.SaveChanges();
return portrait.Id;
}
The About action will look like below.
public ActionResult About()
{
var latestPortraitId = Int.Parse(Session["LatestPortraitId"]);
var about= _portraitRepository.GetContent(latestPortraitId);
}
Inside the repository it should be as shown below.
public Portrait GetContent(int id)
{
return _siteContext.Portraits.Find(id);
}
Order the search by descendent Id then use first property
Related
In my applicantion, I browse to the URL by supplying the parameters through query string. Based on the URI, the respective controller's action is triggered, and the parameters supplied are auto-mapped to my model.
URL: http://{host}:{port}/{website}/{controller}/{action}?{querystring}
URI:
/{controller}/{Action}?{QueryString}
My URI: Employee/Add?EmployeeCode=Code3&EmployeeId=103
EmployeeModel
public class EmployeeModel
{
public Employee()
{
}
public string EmployeeId { get; set; }
public string EmployeeCode { get; set; }
//Some more properties here
}
EmployeeController
[HttpGet]
[Route("Add")]
public IActionResult Add([FromUri] EmployeeModel model)
{
//Some code here
}
While this all works fabulous, when I browse through, below is the order in which break-points hit,
Add method of EmployeeController
Default constructor of EmployeeModel
set method of EmployeeId property of EmployeeModel
set method of EmployeeCode property of EmployeeModel
I suspect the order in which the properties get initialized is based on the order they are declared in the class.
But, to create an instance and initialize the properties the framework must be using reflection. And as per the MSDN documentation for Type.GetProperties the order is not guarateed.
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order. Your code must not
depend on the order in which properties are returned, because that
order varies.
I basically want the initialization to take place in a specific order, is this possible?
You can't get the model binding mechanism to do things in a specific order, but you can make sure that the order is applied where it has to be.
Presumably, EmployeeModel is a domain model object on which the order actually matters, and you're now model binding directly to this type. Instead, introduce an edit model1 which you model bind to, and then map that to your model type:
public class EmployeeEditModel
{
public string EmployeeId { get; set; }
public string EmployeeCode { get; set; }
}
// and change your action signature to this:
[HttpGet]
[Route("Add")]
public IActionResult Add([FromUri] EmployeeEditModel model)
1 For an explanation of what an edit model is, see the final remarks on this old answer of mine.
To perform the mapping you have numerous alternatives, some better than others. Pick one that suits you - however, since the reason the order matters is probably something inherent in the domain model object, I'd advice you to put the logic inside it (e.g. in a constructor), to make it easier to remember to change it if the requirements change.
Map via a constructor on the model object
public class EmployeeModel
{
public EmployeeModel(string employeeId, string employeeCode /* , ... */)
{
// do stuff in whatever order you need
EmployeeId = employeeId;
EmployeeCode = employeeCode;
}
// Now your properties can be get-only
public string EmployeeId { get; }
public string EmployeeCode { get; }
}
Map via an extension method that does everything in the right order
public static class EmployeeEditModelExtensions
{
public EmployeeModel AsDomainModel(this EmployeeEditModel editModel)
{
// do stuff in whatever order you need
var model = new EmployeeModel();
model.EmployeeId = editModel.EmployeeId;
model.EmployeeCode = editModel.EmployeeCode;
// ...
}
// Now your properties can be get-only
public string EmployeeId { get; }
public string EmployeeCode { get; }
}
Use an external framework such as AutoMapper, with custom configuration to make sure that the ordering is correct
Do something else. The only purpose is to get you from an EmployeeEditModel instance to an EmployeeModel instance, assigning to the properties of the EmployeeModel in the correct order. Since you write this code yourself, you can do what you want.
I have a test app that works perfectly with the following classes in one app, but not in another:
public class ValueChange
{
public int GroupId { get; set; }
public List<ItemValueChange> Changes { get; set; }
}
public class ItemValueChange
{
public int ItemId { get; set; }
public string Value { get; set; }
public string Key { get; set; }
}
My plugin posts a JS structure that matches this structure (changes is a jQuery array).
The raw post data (from Fiddler2) looks like:
GroupId 1000
Changes[0][Value]
Changes[0][Key]
Changes[0][ItemId] 1
In the test app this works and maps the data sent to a ValueChange object correctly.
[HttpPost]
public JsonResult Validate(ValueChange change)
{
// The Changes property has the required array of objects/properties
}
In our main application, to which I just ported the plugin and classes, the post data sent looks like:
GroupId 3705
Changes[0][Value]
Changes[0][Key]
Changes[0][ItemId] 81866
and the validate method called looks identical:
[HttpPost]
public JsonResult Validate(ValueChange changes)
{
// changes contains a null list and no GroupId
}
If I break-point this method changes is non-null object with a GroupId of 0 and no child elements in Changes. I can however see these values available from Request.Form in the debugger:
Request.Form["GroupId"] "3705" string
Request.Form["Changes[0][Key]"] "" string
Request.Form["Changes[0][ItemId]"] "81866" string
Request.Form["Changes[0][Value]"] "" string
Q. What would cause the automapping to not work in a different MVC project with the type of data?
If I simplify ValueChange to this (below) it starts working and receives GroupId values:
public class ValueChange
{
public int GroupId { get; set; }
}
If I send JS object data without a changes property it works e.g.
{ GroupId: 123 }
Something about the list called Changes is causing the mapping to fail. I have tried it as an array and also sending a single hard-wired entry from JS like this (still fails):
{ GroupId: 123, Changes: [{ItemId: 456, Value: "V", Key: "K"}]
OMG. The auto-mapper will ignore properties if a property name matches the parameter name!!!
It was caused simply by having the parameter called changes (vs. change in the test app) when a property of the received data was also called changes.
Solution: I changed the parameter name e.g.
[HttpPost]
public JsonResult Validate(ValueChange valueChange)
{
}
To clarify, this problem occurs is a first-level property of the data passed matches a parameter name. If it were a nested property tit would not attempt to match the parameter name.
This little detail needs to be stapled to everyone's desk/hand/head.* :)
I can't seem to get the edit function of my view to work..i have a page that lists, a page that shows specific detail and on that page, i should be able to edit the information of the form..PROBLEM: when i run the application it says:No parameterless constructor defined for this object. What am i doing wrong...?
In the Home Controller i have:
Edit Functions:
[HttpGet]
public ViewResult EditSchoolDetails(int id)
{
var institution = _educationRepository.GetInstititionById(id);
var model = (Mapper.Map<Institution, InstitutionModel>(institution));
return View(model);
}
post
[HttpPost]
public ActionResult EditSchoolDetails( InstitutionModel institutionModel, int id)
{
if (ModelState.IsValid) {
//_get from repository and add to instituion
var institution = _educationRepository.GetInstititionById(institutionModel.Id);
// Map from the view model back to the domain model
var model = Mapper.Map<Institution, InstitutionModel>(institution);
//UpdateModel(model);
SaveChanges();
return RedirectToAction("ViewSchoolDetails", new {institutionModel = institutionModel, id = id});
}
return View(institutionModel);
}
InstitutionModel
public class InstitutionModel {
public InstitutionModel() {
NAABAccreditations = new List<AccreditationModel>();
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsNAAB { get { return NAABAccreditations.Any(); } }
public string Website { get; set; }
public AddressModel Address { get; set; }
public IEnumerable<AccreditationModel> NAABAccreditations { get; set; }
}
Does the Institution class have a parameterless constructor? If not, that will be the problem. You are passing an InstitutionModel to the the edit view, so the post action should probably take an InstitutionModel too, then you can map back to the original Institution model:
public ActionResult EditSchoolDetails(int id, InstitutionModel institutionModel)
{
if (ModelState.IsValid)
{
//add to database and save changes
Institution institutionEntity = _educationRepository.GetInstititionById(institution.Id);
// Map from the view model back to the domain model
Mapper.Map<InstitutionModel, Institution>(institutionModel, institutionEntity);
SaveChanges();
return RedirectToAction("ViewSchoolDetails",);
}
return View(institutionModel);
}
Notice also how it returns the view model back to the view if the model state isn't valid, otherwise you will lose all your form values!
Here's a similar question too which might help: ASP.NET MVC: No parameterless constructor defined for this object
Is it possible you need to pass a parameter to ViewSchoolDetails? I notice in the return statement you commented out that you were passing it an id, but in the return statement you're using, you're not passing in anything.
EDIT
This (from your comment below):
parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult ViewSchoolDetails(Int32)
...tells me you need to pass a parameter to ViewSchoolDetails
EDIT 2
I saw your edit, and would say this: if the method you are calling is
public ActionResult ViewSchoolDetails(InstitutionModel institutionModel, int id)
Then you MUST pass it an object of type InstitutionModel and an int as parameters or you will get an exception. Meaning, you need
RedirectToAction("ViewSchoolDetails", new {institutionModel = institutionModel, id = id});
Whenever i get this, i have forgotten to create a parameter-less constructor on my view-model. I always add one now just in case it's needed and i forget.
Does InstitutionModel have one?
Perhaps there is an easy solution for my problem but I simply cannot seem to find it. I have read lots of tutorials about Knockout so I get the basics but I ask this question because my entity-structure is a bit more complicated than a person with a name and a list of friends which may or may not be on Twitter (Video on Channel9: Helping you build dynamic JavaScript UIs with MVVM and ASP.NET). Here's my situation:
I have a class PersonnelClass with this basic structure:
[Serializable]
//The interface is for the implementation of 'Name' and 'Description'
public class PersonnelClass : IPersonnelClassOrPerson
{
public PersonnelClass() : this(Guid.NewGuid(), "", "") { }
public PersonnelClass(Guid id, String name, String description = null)
{
if (id == Guid.Empty) { throw new ArgumentNullException("id"); }
Id = id;
Name = name;
Description = description;
Properties = new PropertyCollection();
}
public Guid Id { get; private set; }
public String Name { get; set; }
public String Description { get; set; }
public PropertyCollection Properties { get; private set; }
}
The PropertyCollection class and associated AbstractProperty class look like this:
[Serializable]
public class PropertyCollection: List<AbstractProperty> { }
[Serializable]
public abstract class AbstractProperty: IEntity, IProperty
{
public AbstractProperty(String name, String description = null) : this(Guid.NewGuid(), name, description) { }
public AbstractProperty(Guid id, String name, String description = null)
{
if (id == Guid.Empty) { throw new ArgumentNullException("id"); }
if (String.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); }
Id = id;
Name = name;
Description = description;
}
public Guid Id { get; private set; }
public String Name { get; private set; }
public String Description { get; private set; }
}
In my Controller, I create an instance of a PersonnelClassViewModel that has this structure:
public class PersonnelClassViewModel
{
public PersonnelClass PersonnelClass { get; set; }
public List<AbstractProperty> Properties { get; set; }
}
I fill this viewmodel with a new PersonnelClass and two test-properties to pass to my View like this:
var properties = new List<AbstractProperty>
{
new TextProperty("prop1", "descr1"),
new TextProperty("prop2", "descr2")
//TextProperty is derived from AbstractProperty
};
var vm = new PersonnelClassViewModel { Properties = properties };
return View(vm);
I get everything in my View as I wanted. From the View I want to create a new PersonnelClass with a set of selected properties. I have the fields for Name and Description and to add the properties I have a ListBox with the properties that already exist (for demo-purpose they came from the controller now). Through a bit of Knockout JavaScript code I can select items from this list and populate an HTML select-control () with the selected properties to add to the PersonnelClass. This all works fine, until I want to build up an object to pass back to the Controller and create the PersonnelClass.
My question is: what Knockout JS code is needed to build up this object and pass it to the Controller by submitting the form and in my Controller how should I receive this object, meaning: what type of object should this be (PersonnelClass, PersonnelClassViewModel, ...) ?
If any more info/code is needed, please do ask. Thanks in advance!
Update after answer of 'B Z':
I followed a few more of Steven Sanderson's tutorials about this to be sure I understand this, especially the one you provided in your answer. Now I have following code in my View to start with:
var initialData = #Html.Raw(new JavaScriptSerializer().Serialize(Model));
var viewModel = {
personnelClassViewModel : ko.mapping.fromJS(initialData),
properties : personnelClassViewModel.Properties,
selectedProperties : ko.observableArray([]),
addedProperties : ko.observableArray([])
};
ko.applyBindings(viewModel);
The variable 'initialData' contains the values I expect it to have but then I get the following error:
Microsoft JScript runtime error: 'personnelClassViewModel' is undefined
I have no clue anymore. Can anyone help me fix this?
Steven Sanderson has an example of how to to work with variable length lists and knockoutjs
http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/
Having said that, I think your problem isn't so much on the knockout side and more on the how to databind the data correctly on the server side. In the link above, Steven uses a FromJson attribute to model bind which you may find useful...
HTH
I have an EF Code First model that has an ICollection property like so.
public class AccountProfile
{
[Key]
public int AccountID { get; set; }
public string AccountName { get; set; }
public string Email { get; set; }
[Required]
public virtual ICollection<UserProfile> LinkedUserProfiles { get; set; }
}
I'm binding an edit view to this model, but it only shows the AccountName and Email properties.
I have a HttpPost ActionResult to update the model that takes an AccountProfile.
When posting, the AccountProfile object only has the AccountName and Email properties populated. The LinkedUserProfiles is null. This means that the model cannot be updated as the LinkedUserProfiles property is required.
I have also tried something like the following without any luck
var curAccountProfile = _accountProfileRepository.GetById(accountProfile.AccountID);
TryUpdateModel(curAccountProfile);
What am I doing wrong? How should this circumstance be handled in MVC?
UPDATE
The data was coming from a repository eg.
var accountProfile = _accountProfileRepository.ById(1);
return View(accountProfile);
Inspecting the accountProfile object before the view was loaded shows that the collection is being retrieved - so it's not a case of lazy loading not working as expected.
I have since implemented AutoMapper, created a ViewModel for the view, and changed my code to be something like this:
var accountProfile = _accountProfileRepository.ById(accountInformation.AccountID);
var result = _mappingEngine.Map<DisplayAccountInformationViewModel, AccountProfile>(accountInformation, accountProfile);
_unitOfWork.Commit();
It's working as expected now - but it seems like more effort than it should be?
Try eager loading the LinkedUserProfiles:
var curAccountProfile = _accountProfileRepository
.GetById(accountProfile.AccountID)
.Include(ap => ap.LinkedUsedProfiles);
Looks like you're using the Repository pattern, so not sure how you handle eager loading. My Repositories return IQueryable<T>, for which the Include extension methods works off.
If you're not using IQueryable<T>, then you might need to do the eager loading inside the Repository (such as accepting a boolean flag, e.g: GetById(int id, bool include profiles).