Ignore properties on knockout viewmodel when serialized from aspnetmvc - asp.net-mvc

I'm having an ASPNET mvc site hvere the views has viewmodels with knockout. I'm trying to serialize the model passed to the view into the views viewmodel.
The model is passed to the with like standard aspnet mvc
#model Client.Controllers.TripDto
And the knockout viewmodel created using KO mappings is done like
var jsonModel = '#Html.Raw(JsonConvert.SerializeObject(this.Model))';
var mvcModel = ko.mapping.fromJSON(jsonModel);
var viewModel = new TripViewModel();
var mapping = {
'ignore': ["expenses"]
};
ko.mapping.fromJS(mvcModel, mapping, viewModel);
ko.applyBindings(viewModel);
I have a property expenses on the KO "custom" viewmodel. The issue is that this property is not in the MVC model, but just a property that gets its data later.
But so far I haven't been able to pass the model into the viewmodel with out an error
Error: Unable to parse bindings. Message: ReferenceError: expenses is not defined; Bindings value: foreach: expenses
[Break On This Error]
return new Function("$context", "$element", functionBody);
I'm a bit unsure if it really is the mapping that fails, but so far unable to see any uther possible errors.
So the question is, is it possible to use KO mappings where not all propertes are present in the model I'm mapping from?

is it possible to use KO mappings where not all properties are present
in the model I'm mapping from?
I think KO mappings dose not care about this, but the models cares. As you call ko.applyBindings KO 'll search for the property associated with the HTML element, so if you are going to use property in HTML element data-bind='id: property' then this property must exist in your View-Model. But if it was null then it's OK.
Simply you need to extend your view model so include the new property expenses with default value like null
var mapping = {
create: function (options) {
//customize at the root level.
var innerModel = ko.mapping.fromJS(options.data);
innerModel.expenses= null;
return innerModel;
}
}
Kindly check this SO question

Related

MVC Populate dropdownlistfor in CREATE view from another model

So I basically have the same issue as this post: ASP.NET MVC - How to populate dropdownlist with another model?
The solution works... technically.. as long as the model is populated. However, I'm working with a CREATE view where the controller's action simply returns a blank "View()" obviously because it's creating something so you don't have any data yet.... yet I obviously still want the dropdownlist to populate from a model which gets it's data from the DB. If I try to run this code on a view with an empty model I get "Object reference not set to object" when it tries to grab the property that returns the list.
I tried instantiating a new, blank model in the Create action with the USING statement and set only PropertyTypeList property with a new instance of the Type model, and passed it to the view and it sort of worked... the view showed up with the dropdown of the other Type model populated, but it pre-filled in a bunch of the int/date types with 0's and 1/1/1900's because I have non-nullable fields. This is the closest I've gotten so far to simply letting the user create a new record with a pre-populated dropdown from ome of the fields that comes from a model.
I could just create a new Type model in the Create Action and assign it to the Viewbag or Tempdata, which I've done in the past, but this idea just makes me feel DIRTY. Viewbag disappears if the person refreshes the page so they get an error and is totally unprofessional. And I don't use Tempdata much because it relies on session state which gets very problematic if a user has my form open in mulitple tabs which could easily happen.
I feel like the solution from this post is SO close. It works fine when you're working with the EDIT action because you're passing a full model. But does anyone know how to get a dropdownlist to populate like this with an empty model? I tried something like adding an extra class to my secondary Type model
namespace blablanamespace {
public partial class PropertyType {
.. bla bla bla propertytype ID and name here
}
public class ViewModel
{
private readonly List<PropertyType> _keytypes;
public IEnumerable<SelectListItem> PropTypeItems
{
get { return new SelectList(_keytypes, "TypeID", "TypeID"); }
}
}
}
and
#Html.DropDownListFor(model => model.TypeID, new SelectList(new blablanamespace.Models.ViewModel().PropTypeItems))
but then I just get this error:
Value cannot be null. Parameter name: items
If I change the ViewModel class to instantiate a new list of types like so
public class ViewModel
{
public class ViewModel
{
private readonly List<PropertyType> _keytypes = new List<PropertyType>;
public IEnumerable<SelectListItem> PropTypeItems
{
get { return new SelectList(_keytypes, "TypeID", "TypeID"); }
}
}
I don't get the error this time, but I just get a blank form(yay) with a blank dropdownlist(boo!!). I figured this latter method would work since when I want to populate a new fresh list in the controller I basically do the same thing
List<ApplicationKeyType> _keytypes = new List<ApplicationKeyType>();
That behavior doesn't appear to be the same in the model.

How to pass collection of anonymous objects from controller to view

I am new to MVC, so please excuse me if my question sounds silly or too simple. I am using Entity Data Model for database access. So in my Models folder, I have added an EDMX file and I can access the model classes from my controllers and strongly typed views. The problem arises when I access more than one table in my controller e.g.
If I have following tables in my DB :
Departments(DepartmentID, DepartmentName, DepartmentPhone)
Insurances(InsuranceID, InsuranceName, InsuranceAddress)
Employees(EmployeeID, EmpFName, EmpLName, DepartmentID, InsuranceID)
And I want to display a list of Employees with their department and insurance information.
In my Controller's Action Method I access the DB using EDM and get the information in an anonymous type:
using (var context = new MyEntities())
{
var model = (from d in context.Departments
join e in context.Employees on d.DepartmentID equals e.DepartmentID
join I in context.Insurances on I.InsuranceID equals e.InsuranceID
select new
{
DepartmentID = d.DepartmentID,
EmployeeID= e.EmployeeID,
EmpFName= e.EmpFName,
EmpLName= e.EmpLName,
DepartmentName= d.DepartmentName,
InsuranceName= I.InsuranceName
}).ToList();
return View(model);
}
I don't have a class of this anonymous type in my Model folder so I can't create a strongly typed view. So what is the best way to pass this list to the View?. Using viewbag will be an overkill if the collection is too large. Creating a new Model for this anonymous class doesn't sound right as it needs to be updated all the time if I change my selection in Controllers Action Method.
All suggestions are welcomed. I tried looking through other questions on SO but couldn't find anything relevant.
Thanks.
I don't have a class of this anonymous type in my Model folder so I
can't create a strongly typed view
Right click on your project, Add New Class ... and now you have a type in your Model folder. This is the way to go in ASP.NET MVC => view models.
And then obviously you pass this type to your view:
select new MyViewModel
{
DepartmentID = d.DepartmentID,
EmployeeID = e.EmployeeID,
EmpFName = e.EmpFName,
EmpLName = e.EmpLName,
DepartmentName = d.DepartmentName,
InsuranceName = I.InsuranceName
}).ToList();
And of course now your view becomes strongly typed to this view model:
#model IEnumerable<MyViewModel>
...
I'm afraid that predefined strongly-typed ViewModels are the way to go. It is a pain to have to update seemingly duplicate code in multiple places but in my experience it's only a problem for smaller projects. As the project grows you begin to see differences between database model objects (entities) and the viewmodels passed to your views, such as Validation and processing attributes and view-specific data, and when you get to that point you begin to prefer having separate Entity and ViewModel definitions.
However, back on-topic: an alternative solution to your problem is to use reflection to convert an anonymous type into a Dictionary<String,Object> object. Note that ASP.NET MVC does this for converting new { foo = "bar" }-syntax expressions into dictionaries for Route Values and HTML attributes already. Performance is acceptable, but don't try to do it for 10,000 objects for a single HTTP request otherwise you might get bogged down.
Here's what the code for that would look like:
Object anonymousType = new { whatever = "foo" };
Dictionary<String,Object> dict = new Dictionary<String,Object>();
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(anonymousType )) {
Object value = descriptor.GetValue(anonymousType );
dict.Add( descriptor.Name, value );
}
Of course this means that your Views won't benefit from compile-time type-checking and you'll have to maintain a documented list of dictionary keys (assuming you aren't iterating over keys in your view).
I'll note that I am surprised Microsoft didn't make anonymous types automatically implement IDictionary because it seems natural.
dynamic type is your friend.
You can declare your view as loosely typed, having a dynamic as your model
#model dynamic
You will access model properties as you do in strongly typed view
<h1>Model.DepartmentId</h1> - <h2>Model.DepartmentName</h2>
<span>Model.EmployeeId</span>
The problem thought, that dynamics contains internal properties, if you are using MVC2 you need a little trick to make it work. But seems for MVC3 and higher, it's not longer required.

How to bypass the System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method) validation of Entity framework?

I'm using a customized method for tracking individual modified properties of an n-tier disconnected entity class. I extracted it from
Programming Entity Framework: DbContext by Julia Lerman and Rowan
Miller (O’Reilly). Copyright 2012 Julia Lerman and Rowan Miller,
978-1-449-31296-1.
The code is:
public void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState {
// bind the entity back into the context
dbContext.Set<TEntity>().Add(root);
// throw exception if entity does not implement IObjectWithState
CheckForEntitiesWithoutStateInterface(dbContext);
foreach (var entry in dbContext.ChangeTracker.Entries<IObjectWithState>()) {
IObjectWithState stateInfo = entry.Entity;
if (stateInfo.State == RecordState.Modified) {
// revert the Modified state of the entity
entry.State = EntityState.Unchanged;
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
entry.Property(property).IsModified = true;
}
} else {
entry.State = ConvertState(stateInfo.State);
}
}
dbContext.SaveChanges();
}
The purpose of this method is to let the EF know only a predefined set of entity fields are ready for update in the next call of SaveChanges(). This is needed in order to workaround the entity works in ASP.NET MVC 3 as follows:
on initial page load: the Get action of the controller is loading the
entity object and passing it as a parameter to the view.
The View generate controls for editing 2 of the fields of the entity,
and holds the ID of the record in a hidden field.
When hitting [save] and posting the entity back to the controller all
of the fields excepting the 3 preserved in the view comes with a null
value. This is the default behavior of the MVC binding manager.
If i save the changes back to the database the update query will of course overwrite the non mapped fields with a sentence as follows:
UPDATE non_mapped_field_1 = NULL, ..., mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2', ... non_mapped_field_n = NULL WHERE ID = mapped_field_3
This is the reason i'm trying to track the fields individually and update only those fields i'm interested in. before calling the custom method with ApplyChanges() i'm adding the list of fields i want to be included in the update to the IObjectWithState.ModifiedProperties list, in order to get a SQL statement as follows:
UPDATE mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2' WHERE id = mapped_value_3
The problem is, when marking one of the fields as modified in ApplyChanges, i.e.:
entry.Property(property).IsModified = true;
the system is throwing the following exception:
{System.InvalidOperationException: Member 'IsModified' cannot be called for property 'NotifyCEDeadline' on entity of type 'User' because the property is not part of the Entity Data Model.
at System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method)
at System.Data.Entity.Internal.InternalPropertyEntry.set_IsModified(Boolean value)
at System.Data.Entity.Infrastructure.DbPropertyEntry.set_IsModified(Boolean value)
...
So the question is. There's a way to bypass this EF validation or let the context know of the existance of this system property (IsModified) that i'm trying to change?
Summary of the architeture:
EF Code first (annotation + Fluent API)
Oracle .NET EF Data provider (ODAC)
Context is injected to a cutom business context with nInject.MVC => this is the reason i customized the ApplyChanges() method from
using (var context = new BreakAwayContext()){
context.Set().Add(root);
to a simple call to the already initialized dbcontext
dbContext.Set().Add(root);
Oracle Database is created manually i.e. without the help of EF, so no EF metadata tables are used.
Thanks,
Ivan.
Very good description, however I can't find any information on why you need a transient property called "IsModified" in the object and/or why you need to tell EF about it being modified (EF won't be able to persist it anyway).
The value of the IsModified property should be set by the model binder if the property was incldued in the view anyway.
You could just add code in your ApplyChanges method to skip a property named "IsModified", or even better, filter only known properties using entry.CurrentValues.PropertyNames, e.g.:
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
if (entry.CurrentValues.PropertyNames.Contains(property)) {
entry.Property(property).IsModified = true;
}
}
Update: Ivan, very sorry I did not understand the problem better when you posted it several months ago and that I did not follow up after your added these clarifying comments. I think I understand better now. That said, I think the code snippet that I offered can be part of the solution. From looking at the exception you are getting again, I understand now that the problem that EF is detecting is that NotifyCEDDealine is not a persistent property (i.e. it is not mapped in the Code First model to a column in the database). IsModified can only be used against mapped properties, therefore you have two options: you change the code of the implementation of IObjectWithState in your entities so that non-mapped properties are not recorded in ModifiedProperties, or you use my code snippet to prevent calling IsModified with those.
By the way, an alternative to doing all this is to use the Controller.TryUpdateModel API to set only the modified properties in your entities.
Hope this helps (although I understand it is very late).

Model Binding With Entity Framework (ASP.NET MVC)

Earlier I created an AddClient page that (when posted) passed a client object and I used db.AddToClient(obj) in my repository to persist it. Easy stuff.
Now, I have a details page whose save submits a post to an action "UpdateClient". Before that action is hit, my custom model binder creates my Client object and it's conveniently handed to the action. The thing is, this client object is not connected to a EF context yet. Where is the correct place to do that? In the modelbinder, or maybe when we get it from the controller, or perhaps we wait until we do a repository call and link it up there? What's the recommended process?
From what I remember, you will either need to attach the object again to the context and set it as modified
Or reload the object from the database and apply your changes.
This article explains it better:
http://msdn.microsoft.com/en-us/magazine/ee321569.aspx#id0090022
What version of EF are you using ?
If an EF object has been created outside a context, you need to Attach the object to the context.
See: http://msdn.microsoft.com/en-us/library/bb896271.aspx
I know this is an old thread but in the interest of new readers:
Based on my observations using VS 2012, MVC 4 and EF 4.0 with a view that has an EF object for a model that submits a form back to the controller.
On the controller:
public ActionResult SubmitEFObject(tblData data, FormCollection col)
"data" will only have the properties used in the view (#Html.xxxFor) filled.
It appears that when "data" is created, the posted FormCollection is used to set data's properties. If you had a property that wasn't used, DataID for example, then data.DataID will have a null/default value. Add a "#Html.Hidden(m => m.DataID)" to your view and THEN DataID will be filled.
As a 'quick n dirty' way to work with this, I created a method that would merge the incoming 'data' with the 'data' in the database and return the merged object:
// Note: error handling removed
public tblData MergeWithDB(DBContext db, tblData data, params string[] fields)
{
tblData d = db.tblData.Where(aa => aa.DataID == data.DataID).Single();
if (fields.Contains("Field1")) d.Field1 = data.Field1;
if (fields.Contains("Field2")) d.Field2 = data.Field2;
if (fields.Contains("Field3")) d.Field3 = data.Field3;
// etc...
return d;
}
On the controller:
public ActionResult SubmitEFObject(tblData data, FormCollection col)
{
DataEntities db = new DataEntities();
tblData d = MergeWithDB(db, data, col.AllKeys);
db.SaveChanges();
}
You could make this more generic using reflection or maybe more efficient by looping through the string[] fields instead of all the ifs but for my purposes this was 'good enough'.
Database work should be put in the repository call.
Are you directly binding to an entity framework object in your model binding? If not, you should consider do some mapping between your custom object and entity framework object.

Using LLBL as Model in MVC

I have settled on trying to use ASP.NET MVC but the first part I want to replace is the Model. I am using LLBL Pro for the model.
I have a table called "Groups" that is a simple look up table. I want to take thhe results of the table and populate a list in MVC. Something that should be very simple... or so I thought.... I've tried all kinds of things as I was getting errors like:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[glossary.CollectionClasses.GroupCollection]'.
private GroupCollection gc = new GroupCollection();
public ActionResult Index()
{
gc.GetMulti(null);
return View( gc.?????? );
}
This is all I am trying to do, I've tried lots of variations, but my goal is simply to take the data and display it.
Not sure if this would work, but you could try wrapping the EntityCollection into a ViewModel class and passing it to the View like so:
public class GroupsViewModel()
{
public GroupCollection Groups { get; set; }
// other items in your view model could go here
}
then convert your controller method to
public ActionResult Index()
{
GroupCollection gc = new GroupCollection();
gc.GetMulti(null);
GroupsViewModel vm = new GroupsViewModel();
vm.Groups = gc;
return View(vm);
}
I like this approach because each ViewModel is an object in-and-of itself.
You can use the AsEnumerable extension where your ????? are or change the type of your ViewUserControl(in the markup) to be of type System.Collections.Generic.List. Basically what you need to correct is the mismatch between the type of the View and the Model being passed in.
I'm not sure about your exact error, but I'd venture a guess that one of two things are happenging:
You are making some sort of invalid / illegal call on your LLBLGen object. If this is the case make sure you are setting it up right / calling right method / property etc.
The model you are passing to the veiw is too hairy for it to deal with. In this case, and in general, you should create a light 'View Model' class with just the data you want displayed and populate it from your LLBLGen object first then pass it to the view, which will be able to easily handle your view model class.
Here are some references:
http://stephenwalther.com/blog/archive/2009/04/13/asp.net-mvc-tip-50-ndash-create-view-models.aspx
http://nerddinnerbook.s3.amazonaws.com/Part6.htm
http://www.codinginstinct.com/2008/10/view-model-inheritance.html
Stemming off what Yuriy said, it looks like your view is strongly typed to a "collection" of a collection of your groupentity, and you are trying to pass just the collection of your groupentities. Make sure your "collection" type (IEnumerable, IList, etc) matches what type of collection you are sending in your controller, along with the type of the actual object in the collection.
View:
System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]
Controller:
System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]
Just a thought

Resources