AutoMapper UseValue, ResolveUsing or MapFrom reusing value from previous call. Is it caching or something? - ajax

I have a situation where starting from the 2nd call and subsequent ones (Ajax GET calls), AutoMapper is reusing the previous value (the value from the 1st call that comes from a click in an action link). It's like a "caching" problem...
public virtual ActionResult List(int assessmentId, int? chapterId, bool? isMenuClick)
{
Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => assessmentId));
...
}
It doesn't matter if I use UseValue, ResolveUsing or MapFrom in the above opt => lambda. The behavior is the same, that is, it reuses the value from previous calls.
AssessmentId property does not exist in the source type ( Element ). This way I try to assign AssessmentId a value that "may" change dynamically during subsequent calls to the method where I have this code. assessmentId is a parameter in my ASP.NET MVC action method as shown above in the method signature.
Then I call this code in the List action method:
var questions =
Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
(Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
e.Standard.ChapterId == chapterId));
The first time, questions is OK, that is, all AssessmentQuestionViewModel objects have the AssessmentId property set correctly as per the CreateMap defined.
Starting from the 2nd call, it reuses the assessmentId from the 1st call and it messes up with my business logic because I expect it to map AssessmentId to the updated assessmentId that's being passed as a parameter to the List method.
Just to be sure: I've set a breakpoint in the code and I can see that the value of the assessmentId parameter is correct. It's just the returned mapped objects questions that have the wrong value in the AssessmentId property - a value that differs from the current assessmentId value. The values should be equal as I understand it since I'm asking AutoMapper to do the mapping using that current value.
I have AutoMapper 2.2.1-ci9000 (Prerelease), but I tested this with the previous version and I saw this same behavior. I updated to the Prerelease thinking that this "misbehavior" would go away.
I think this is a bug. Please correct me if I'm wrong or if I'm trying to use it in a way not supported. :)

I think the problem here your trying to create multiple mappings of the same type - which AutoMapper doesn't support. Everytime your List action is called, you create a new mapping (which has a different ForMember(...) clause). AutoMapper won't throw an exception it just ignores the duplicate mapping so what you are seeing here isn't a bug, it's expected behaviour.
ForMember is infact called on every map, however, you have a scoping issue here as your variable is hard-coded into the expression. As a work-around you could do something like:
public class MyController
{
public MyController()
{
// define mapping once, but make assessment expression dynamic
Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => GetCurrentAssessmentId()));
}
private int GetCurrentAssessmentId()
{
return (int)TempData["AssessmentId"];
}
public ActionResult List(int assessmentId, ...)
{
// store current assessment temporarily
TempData.Add("AssessmentId", assessmentId);
// execute mapping
var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
(Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
e.Standard.ChapterId == chapterId));
}
}
I will say though, your jumping through a lot of hoops for this to work, it would be much simpler to manually set the property without the help of AutoMapper e.g.
var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>(...);
foreach (var q in questions)
{
q.AssessmentId = assessmentId;
}

Related

Entity being tracked despite AsNoTracking

I have an object, Client, with a navigation property that is a list of Order objects. Whenever I retrieve a Client object, I include the list of Orders, with AsNoTracking().
public new IQueryable<Client> FindByConditionNoTracking(Expression<Func<Client, bool>> expression)
{
return this.ClientContext.Set<Client>().Include(s => s.Orders)
.Where(expression).AsNoTracking();
}
In my UpdateClient repository method, I take in a Client object. I then attempt to retrieve that original client from the database (using Include to get the child Orders), map the Client param to the original, and save to the database. Over here, I do not use AsNoTracking, because I specifically want the changes to be tracked.
public new void Update(Client client)
{
var id = client.ClientId;
var original = this.ClientContext.Clients.Include(s => s.Orders).Where(s => s.ClientId == id)
.FirstOrDefault<Client>();
original = _mapper.Map(client, original);
this.ClientContext.Update(original);
}
The error I am getting is that an instance of Order with the same key value is already being tracked. A few problems with that:
Wherever the Client and the child Orders are retrieved for the purposes of display I use AsNoTracking.
The only place where I retrieve without AsNoTracking is where I get the original within this very method.
The bug isn't with the parent property. If I was improperly retrieving the Client elsewhere, wouldn't I have this error with the Client id itself? But the error seems to be only with the navigation property.
All insight is appreciated!
If anyone else runs into this: Automapper, when mapping collections, apparently recreates the entire collection. I solved the above issue by using Automapper.Collections in my mapping configuration. Thanks to Mat J for the tip!

Enforce ordering of OData items even when $top is used

I have a DbSet<Items> collection.
The primary key is a Guid. I don't want to order by this primary key. I want to order by an editable decimal property named "Order".
The code I have is very simple, and it works great until the user puts a "$top" parameter into the request:
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
// GET api/documents
[EnableQuery()]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
When the user puts "$top" into the query string, the order gets all messed up (it presumably forces the ordering to be done by the primary key, for consistent paging results -- however, in my situation, this is having the opposite effect, it's preventing me from having consistent paging results).
I've tried moving .AsQueryable() to be earlier in the query (before the .OrderBy(...) clause), I've tried it without the .AsQueryable(), I've tried it with two AsQueryables, etc.
There are going to be a lot of items in this table, so it needs to be done via an IQueryable (enumerating all of the items on the web server via IEnumerable is not an option here).
The only thing that has worked so far is passing in "$orderby=Order" from the client, but I don't want to force that (seems like it will get forgotten easily).
1.) Is there anything I can do to make ordering by my Order property the default behavior here?
2.) Or failing that, is there anyway to trick WebApi / OData into thinking that a custom "$orderby=Order" clause was specified?
To override default sort order, you need to set property EnsureStableOrdering of EnableQueryAttribute to false, like describe here:
A true value indicates the original query should be modified when
necessary to guarantee a stable sort order. A false value indicates
the sort order can be considered stable without modifying the query.
Query providers that ensure a stable sort order should set this value
to false. The default value is true.
So in your code, changes the action attribute like this:
// GET api/documents
[EnableQuery(EnsureStableOrdering = false)]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
You can manually invoke the odata in your controller. This should create the proper sorted IQueryable and then apply the $top and any other odata like $filter and $skip. Now you don't have to return an IQueryable which was causing the problem because the actual query was being executed later in the pipeline.
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
public IEnumerable<Item> Get(ODataQueryOptions<Item> odata)
{
var collection = ctx.Items.OrderBy(o => o.Order);
if (odata == null)
{
//return a default max size of 100
return collection.Take(100).ToList();
}
var results = odata.ApplyTo(collection.AsQueryable()) as List<Item>;
//still provide a max incase the $top wasn't specified.
//you could check the odata to see if $top is there or not.
return results.Take(100);
}
}
More information can be found in the WebApi documentation.

Proper way to Edit an entity in MVC 3 with the Entity Framework using Data Model First approach?

A majority of the examples I see now are either using the Code First Approach or using an older version of MVC and the Entity Framework.
Assume I have a movie to update and I get to the Edit View, in the Edit method with the Post verb, what is the proper way to update a Movie? The first Edit Method below gets me to the Edit View with the populated Movie values and the second one is the one I want to use to update, I have tried some things, but nothing updates the data.
public ActionResult Edit(int id)
{
var movie = (from m in _db.Movies1
where m.Id == id
select m).First();
return View(movie);
}
[HttpPost]
public ActionResult Edit(Movie movie)
{
try
{
// TODO: Add update logic here
//What do I need to call to update the entity?
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Assuming that _db is derived from ObjectContext you have two options:
Change the state of the entity to Modified:
_db.Movies1.Attach(movie);
_db.ObjectStateManager.ChangeObjectState(movie, EntityState.Modified);
_db.SaveChanges();
This marks all properties of movie as modified and will send an UPDATE statement to the database which includes all column values, no matter if the values really changed or not.
Reload the original entity from the database and apply the changes to it:
var originalMovie = (from m in _db.Movies1
where m.Id == movie.Id
select m).First();
// You actually don't need to assign to a variable.
// Loading the entity into the context is sufficient.
_db.Movies1.ApplyCurrentValues(movie);
_db.SaveChanges();
ApplyCurrentValues will mark only those properties as modified which really did change compared to the original and the UPDATE statement which will be sent to the database only includes the changed column values. So, the UPDATE statement is potentially smaller than in the first example but you have to pay the price to reload the original entity from the database.
Edit
How does the second code example work?
When you run a query using the context (_db) Entity Framework does not only retrieve the entity from the database and assign it to the left side of the query (originalMovie) but it actually stores a second reference internally. You can think of this internal context "cache" as a dictionary of key-value pairs - the key is the entity primary key and the value is the entity itself, the same object as originalMovie refers to.
ApplyCurrentValues(movie) looks up this entity in the context's internal dictionary: It takes the key property value Id of the passed in movie, searches for an entity with that key in the internal dictionary and then copies property by property from the passed in ("detached") movie to the internal ("attached") entity with the same key. EF's change tracking mechanism marks the properties as Modified which were actually different to create later the appropriate UPDATE statement.
Because of this internal reference to the original entity you do not need to hold your own reference: That's the reason why originalEntity is not used in the code. You can in fact remove the assignment to the local variable altogether.
The example would not work if you disable change tracking when you load the original entity - for example by setting _db.Movies1.MergeOption = MergeOption.NoTracking;. The example relies on enabled change tracking (which is the default setting when entities are loaded from the database).
I cannot say which of the two examples has better performance. That might depend on details like size of the entities, number of properties which have been changed, etc.
It's worth to note though that both approaches do not work if related entities are involved (for example movie refers to a category entity) and if the relationship or the related entity itself could have been changed. Setting the state to Modified and using ApplyCurrentValues both affect only scalar and complex properties of movie but not navigation properties.
Your second edit method should look something like this:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var movie = (from m in _db.Movies1
where m.Id == id
select m).First();
if (TryUpdateModel(movie))
{
_db.SaveChanges();
return (RedirectToAction("Index"));
}
return View(movie);
}

MVC 3 Details View

I am new to MVC frame work. And i am making one page where we can see details of department by clicking on details link button.
While User click link button it fetch the all the records of the particular department in List Collection and redirect to Details View.Data has been fetched in List but while going to Details view it Generates following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[DocPageSys.Models.Interfaces.DepartmentInfo]', but this dictionary requires a model item of type 'DocPageSys.Models.Interfaces.DepartmentInfo`'.
I understood the error but confusion to solve it.And stuck with this problem...
Since your Details view is strongly typed to DepartmentInfo:
#model DocPageSys.Models.Interfaces.DepartmentInfo
you need to pass a single instance of it from the controller action instead of a list:
public ActionResult Details(int id)
{
DepartmentInfo depInfo = db.Departments.FirstOrDefault(x => x.Id == id);
return View(depInfo);
}
So make sure that when you are calling the return View() method from your controller action you are passing a single DepartmentInfo instance that you have fetched from your data store.
To make it run fine initially you could simply hardcode some value in it:
public ActionResult Details(int id)
{
var depInfo = new DepartmentInfo
{
Id = 1,
Name = "Sales",
Manager = "John Smith"
}
return View(depInfo);
}
Oh, and you will notice that I didn't use any ViewData/ViewBag. You don't need it. Due to their weakly typed nature it makes things look really ugly. I would recommend you to always use view models.
Passing a list instead of a single item
This error tells you, that you're passing a list to your view but should be passing a single entity object instance.
If you did fetch a single item but is in a list you can easily just do:
return View(result[0]);
or a more robust code:
if (result != null && result.Count == 1)
{
return View(result[0]);
}
return RedirectToAction("Error", "Home");
This error will typically occur when there is a mismatch between the data that the controller action passes to the view and the type of data the view is expecting.
In this instance it looks as if you're passing a list of DepartmentInfo items when your view is expecting a single item.

Storing Session Variables in Lift Scala

I am trying to store a session variable and then use it to modify the menu in Boot.scala. Here is how I am storing the variable in a snippet:
object sessionUserType extends SessionVar[String](null)
def list (xhtml : NodeSeq) : NodeSeq = {
Helpers.bind("sendTo", xhtml,
"provider" -> SHtml.link("/providerlogin",() => sessionUserType("provider"), Text("Provider")),
"student" -> SHtml.link("/studentlogin",() => sessionUserType("student"), Text("Student")))
}
Then in Boot.scala I do this:
val studentSessionType = If(() => S.getSessionAttribute("sessionUserType").open_!.equals("student"),
"not a student session")
I also tried to call the object by name (sessionUserType), but it can never find it, so I thought this might work, but I keep getting an empty box when i access it even though the actual binding and function get executed prior to the menu rendering.
Any help would be much appreciated.
Thanks
In order to get a value from SessionVar or RequestVar, call is method on it, i.e. sessionUserType.is
By the way, did you read "Managing State"?
Side note
I believe RequestVar is a better fit in your case. I am not sure I could catch your code right without the context, but it could be rewritten at least like:
case class LoginType
case object StudentLogin extends LoginType
case object ProviderLogin extends LoginType
object loginType extends RequestVar[Box[LoginType]](Empty)
// RequestVar is a storage with request-level scope
...
"provider" -> SHtml.link("/providerlogin",() => loginType(Full(ProviderLogin)), Text("Provider")),
// `SHtml.link` works in this way. Your closure will be called after a user
// transition, that is when /providerlogin is loading.
...
val studentSessionType = If(() => { loginType.is map {_ == StudentLogin} openOr false },
"not a student session")
// As a result, this test will be true if our RequestVar holds StudentLogin,
// but it will be so for one page only (/studentlogin I guess). If you want
// scope to be session-wide, use SessionVar
I think the disconnect is that you are assuming that the a Session Attribute is going to match your SessionVar, and that is not the case. Lift is a very secure framework, and one if its features is that Lift creates opaque GUIDs to refer to components on the
server side.
If you want getSessionAttribute to return something, think about how you can call setSessionAttribute.

Resources