Return data from OData Action using OData Client - return

I am testing the OData Action using ODataActionsSample, which is downloaded from http://aspnet.codeplex.com/sourcecontrol/latest#Samples/WebApi/OData/v4/ODataActionsSample/ODataActionsSample/, as a server and calling the "CheckOut" action which is,
[HttpPost]
public IHttpActionResult CheckOut(int key)
{
var movie = _db.Movies.FirstOrDefault(m => m.ID == key);
if (movie == null)
{
return BadRequest(ModelState);
}
if (!TryCheckoutMovie(movie))
{
return BadRequest("The movie is already checked out.");
}
return Ok(movie);
}
The action returns the movie with updated "DueDate" proprety in the sample program which is calling the action from javascript as below:
// Invoke "checkout" or "return" action. Both actions take no parameter data.
function invokeAction(url) {
ajaxRequest("post", url)
.done(function (updated) {
updateMovie(updated);
})
.fail(function (jqXHR, textStatus, errorThrown) {
//parent.errorMessage(errorThrown);
parent.errorMessage(url);
});
}
self.update(data);
// Update the model with new data from the server.
function updateMovie(data) {
var dueDate = data.DueDate ? new Date(data.DueDate) : null;
self.dueDate(dueDate);
if (data["#ODataActionsSample.Models.CheckOut"]) {
self.checkoutUrl(data["#ODataActionsSample.Models.CheckOut"].target);
}
else {
self.checkoutUrl(null);
}
if (data["#ODataActionsSample.Models.Return"]) {
self.returnMovieUrl(data["#ODataActionsSample.Models.Return"].target);
}
else {
self.returnMovieUrl(null);
}
}
However, the call from OData Client returns the movie without the DueDate updated. The client code is as below:
string serviceUri = "http://localhost:44221/OData/";
var container = new Container(new Uri(serviceUri));
var movieQuery = from movie in container.Movies select movie;
DataServiceCollection<ODataActionsClient.Movie> trackedMovies = new DataServiceCollection<ODataActionsClient.Movie>(movieQuery, TrackingMode.AutoChangeTracking, "Movies",null,null);
var myMovie = trackedMovies[0];
try
{
var checkouttedMovie = myMovie.CheckOut().GetValue();
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
What is wrong with my code in the client side ?

The default value for merge option is AppendOnly : No current values are modified.
OverwriteChanges : All current values are overwritten with current store values, regardless of whether they have been changed.
PreserveChanges: Current values that have been changed are not modified, but any unchanged values are updated with the current store values. No changes are lost in this merge.
So you have to decide which option you want to achieve, in this case I think OverwriteChanges is good enough.
FYI : https://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption(v=vs.110).aspx

Related

Handling Next Record nullable reference Controller Code to pass to viewbag

I have researched various nullable reference handling posts, but not finding anything helpful. So what I am doing below to handle this null reference (it's a hack for now to stop the error page from displaying to users) is to essentially return the current id if a next record does not exist in my edit controller.
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var myInClause = new string[] { "a", "c", "k" };
var myQueryResults = await _context.MyClass.FindAsync(id);
int? NextIdObject = (from i in _context.MyClass
where myInClause.Contains(i.RowType) && (i.Id > myclass.Id)
select new { i.Id }).DefaultIfEmpty().First().Id;
if (!NextIdObject.Equals(0))
{
ViewBag.nextID = NextIdObject;
}
else
{
ViewBag.nextID = id;
}
if (myQueryResults == null)
{
return NotFound();
}
return View(myQueryResults);
}
I would prefer to just redirect to the index page (if they hit this error, it means they are done working through a queue anyway, no next record would ever exist here). Or maybe just keep the code as is and display a message on the button to indicate end of list. Any thoughts here. Note, using any +1 increment on the id does not work for me, as I don't need the user to see all id records, just the one's with a/c/k which is why I bring the myInclause variable in. If there is a better way to use the SQL sytanx of "IN" for Linq queries, I am all ears.
I would prefer to just redirect to the index page (if they hit this
error, it means they are done working through a queue anyway, no next
record would ever exist here)
You could use try...catch block simply like
try
{
var myInClause = new string[] { "a", "c", "k" };
var myQueryResults = await _context.MyClass.FindAsync(id);
int NextIdObject = (from i in _context.MyClass
where myInClause.Contains(i.RowType) && (i.Id > myclass.Id)
select new { i.Id }).DefaultIfEmpty().First().Id;
ViewBag.nextID = NextIdObject;
}
catch(Exception ex)
{
return RedirectToAction("Index");
//or return View("Index");
}
return View(myQueryResults);

Kendo Upload control remove event has no response

I have a kendo upload control like this:
#(Html.Kendo().Upload()
.Name("attachments")
.Async(a => a
.Save("UploadAsync", "Intel")
.Remove("RemoveAsync", "Intel")
.AutoUpload(true)
)
.Events(e => e
.Success("onSuccessfulUpload")
.Remove("onRemoveFile")
)
.Validation(v => v.AllowedExtensions(exts))
)
In the controller, its Save method is like this:
public ActionResult UploadAsync(IEnumerable<HttpPostedFileBase> attachments)
{
string filename;
// ... do things ...
return Json(new { ImageName = filename }, "text/plain");
}
where the variable filename is assigned a value.
Its Remove method in the controller looks very similar:
public ActionResult RemoveAsync(string[] fileNames)
{
string filename;
// ... do things ...
return Json(new { ImageName = filename }, "text/plain");
}
I verified that both controller methods are called correctly and the variable filename is assigned to in both cases.
The upload works as expected, and the Success event also works as expected. (The alert is simply for testing.)
function onSuccessfulUpload(e) {
alert(e.response.ImageName);
}
The issue comes on removal of a file.
When I get to the Remove event, e does not have a .response. It has e.files and e.sender, but no response.
function onRemoveFile(e) {
alert(e.response); // undefined!
alert(JSON.stringify(e.files)); // works, but does not have what I need
}
How do I access what the RemoveAsync method returns?
It looks like the remove event doesn't provide this kind of data, so I see only a workaround to solve this.
You could try to put the result name to the headers, and you should be able to read the result:
// Controller
Response.AddHeader("ImageName", imageName); // before Json(...)
// View/JS
alert(e.headers['ImageName']);
I haven't tested that and I see a risk that that the remove event doesn't really read the async response, that would explain why the response object is not available.
In that case, you could try to use the following workaround: Don't call any Url on remove (or use some Action without any body, just a plain result) and inside of the event callback, execute RemoveAsync yourself.
// View/JS
function onRemoveFile(e) {
$.post('#Html.Url("RemoveAsync", "Intel")', e.files, function(response) {
alert(response);
});
}
It's not pretty, but it should work and provide the results you need.
After some time poking around, I found the answer.
The key lay in the order of the events. My first assumption was that the Success event was called after successful upload, and the Remove event was called after successful(?) removal. This was wrong.
The actual order of the events is:
JS onUpload > Controller UploadAsync > JS onSuccess
JS onRemoveFile > Controller RemoveAsync > JS onSuccess
My Solution:
I created two parallel arrays in javascript to represent the files uploaded in the client-side e.files, which contains uid's for each file, and the filenames created by the server-side controller method (which renames the files).
var fileUids = [];
var fileSaveNames = [];
I changed the onSuccessfulUpload function to this, when I discovered that there is an e.operation that specifies which operation was the successful one:
function onSuccess(e) {
if (e.operation == "upload") {
var filename = e.response.ImageName;
var uid = e.files[0].uid;
// add to the arrays
fileUids.push(uid);
fileSaveNames.push(filename)
// ...
}
else if (e.operation == "remove") {
var uid = e.files[0].uid;
var saveIdx = fileUids.indexOf(uid);
// remove from the arrays
fileSaveNames.splice(saveIdx, 1);
fileUids.splice(saveIdx, 1);
// ...
}
}
Then I updated the removeFile function, which I now knew was called before the method in the controller.
function removeFile(e) {
var uid = e.files[0].uid;
var idx = fileUids.indexOf(uid);
e.data = { fileToRemove: fileSaveNames[idx] };
}
That last line, where I assign to e.data, was because of this thread on the Telerik forums, which has the following info:
Solution: All that's needed it to define a function for the upload
event and modify the "data" payload.
Add the upload JS function to add a parameter "codeID" in my case.
$("#files").kendoUpload({
[...]
upload: function (e) {
e.data = { codeID: $("#id").val() };
}
});
Now on the controller add the parameter and that's it.
[HttpPost]
public ActionResult Save(IEnumerable<HttpPostedFileBase> files, Guid codeID) { }
(Instead of being in the Upload event, mine is in the Remove event.)
I chose the parameter name fileToRemove, and now the new RemoveAsync method in the controller is as such:
public ActionResult RemoveAsync(string[] fileNames, string fileToRemove)
{
string returnName = "";
if (!string.IsNullOrWhiteSpace(fileToRemove))
{
// ... do things ...
}
return Json(new { ImageName = returnName }, "text/plain");
}

Value Not Included When Mapping in AutoMapper

I have an action like following:
public JsonResult Update(UpdateUserViewModel updateUser)
{
try
{
var existUser = _uow.Users.GetById(updateUser.UserId);
AutoMapper.Mapper.CreateMap<UpdateUserViewModel,User>();
var model = AutoMapper.Mapper.Map<User>(updateUser);
_uow.Users.UpdateEntity(model);
_uow.Save();
return Json(new { Result = "OK" }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
UpdateUserViewModel does not have a Password property but User does and it is being filled in existUser. The resultant model includes Password property but not the value that was in existUser.
I do not what I am doing wrong. Please point me to right direction.
When you map with
var model = Mapper.Map<User>(updateUser);
Then new instance of destination User object is created and populated with data from updateUser. You should map from source object to existing object instead:
var existUser = _uow.Users.GetById(updateUser.UserId);
var model = Mapper.Map(updateUser, existUser);
// you even don't need model here
// just call Mapper.Map(updateUser, existUser) and use existUser
Thus AutoMapper will use existing instance of User and it will update it with data from UpdateUserViewModel.
NOTE: It's better to create mappings once on application startup.

Possible Breeze 1.4.8 Bug? fetchEntityByKey not waiting for metadata

I have the following code that went from this:
var getUserByGuid = function (guid, entityObservable) {
return datacontext.manager.user.fetchEntityByKey('User', guid, true)
.then(fetchSucceeded)
.fail(queryFailed);
function fetchSucceeded(data) {
var entity = data.entity;
if (ko.isWriteableObservable(entityObservable))
entityObservable(entity);
return entity;
}
function queryFailed(error) {
logger.error(error);
}
};
to this:
var getUserByGuid = function (guid, entityObservable) {
if (datacontext.manager.user.metadataStore.isEmpty()) {
return datacontext.manager.user.metadataStore.fetchMetadata('breeze/user')
.then(function () {
return datacontext.manager.user.fetchEntityByKey('User', guid, true)
.then(fetchSucceeded)
.fail(queryFailed);
});
} else {
return datacontext.manager.user.fetchEntityByKey('User', guid, true)
.then(fetchSucceeded)
.fail(queryFailed);
}
function fetchSucceeded(data) {
var entity = data.entity;
if (ko.isWriteableObservable(entityObservable))
entityObservable(entity);
return entity;
}
function queryFailed(error) {
logger.error(error);
}
};
Notice the extra check to verify metadataStore is ready? Since I am making a call to fetch, I would assume this check would happen internally but for some reason it is not.
My code runs well with the following "work-around" in place but wanted to bring this to light.
Updated 3/1/2014
As of Breeze 1.4.9 (or later), available now this has been fixed.
Previous post
I think you are right. The problem, I think, is that fetchEntityByKey doesn't actually have to perform a fetch when you tell it to search the local cache first. But in this case, if you don't have metadata then the localQuery fails. I'll try to get this fixed in the next release, probably out later this week.

How to call a controller method from JavaScript when project is run under the IIS?

I want to call the JavaScript function for counting the selected checkbox and in this function I have to call one statement for call the actionresult method of controller and perform to some function as active user and deactive user and return view() at end
Here is my code:
if (state == "Dec") {
alert("Hello..Dear..you are DeActive");
$.post('#Url.Action("UserDeactive","Admin", new{})' + '?Id=' + strvalue);
}
This statement works well in the normal project when I run project by press the F5, also using VS localhost.
But when I host my project in IIS, for accessing in LAN, this is not work hold java script call but this statement are not call and not navigate to action result method ..for do some function..!
so please me ...All My Dear..! if you have any idea .!
this is my Controller Method:-
public ActionResult UserActive(string Id)
{
int[] numbers = Id.Split(',').Select(n => int.Parse(n)).ToArray();
if (numbers != null)
{
foreach (int id in numbers)
{
User_Master u_master = db.User_Masters.Find(id);
if (u_master.Is_active == "false")
{
u_master.Is_active = "true";
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
}
}
return RedirectToAction("Dashboard", "Home");
}
Use the second argument of the $.post() method which allows you to POST additional parameters to the server:
var url = '#Url.Action("UserDeactive", "Admin")';
$.post(url, { id: strvalue }, function(result) {
// handle the result of the AJAX call
});

Resources