I'm looking to see if there is a way to eliminate one of the two calls that gets made to my method to google maps to calculate long/lat coordinates.
Here is my method.
public static GeocoderCoordinates GetCoordinates(string region)
{
WebRequest request = WebRequest.Create("http://maps.googleapis.com/maps/api/geocode/xml?sensor=false&address=" + HttpUtility.UrlEncode(region));
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
XDocument document = XDocument.Load(new StreamReader(stream));
XElement longitudeElement = document.Descendants("lng").FirstOrDefault();
XElement latitudeElement = document.Descendants("lat").FirstOrDefault();
if (longitudeElement != null && latitudeElement != null)
{
return new GeocoderCoordinates
{
Longitude = Double.Parse(longitudeElement.Value, CultureInfo.InvariantCulture),
Latitude = Double.Parse(latitudeElement.Value, CultureInfo.InvariantCulture)
};
}
}
}
return null;
}
The first time I call this method it's for validation.
internal class ValidateLocationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var location = value as string;
GeocoderCoordinates coordinates = Geocoding.GetCoordinates(location);
if (coordinates == null)
return false;
return true;
}
}
and if there is no location that gets found it returns null - validation fails.
The second time it gets called is in the controller to set longitude/latitude coordinates within my entity.
[HttpPost]
public ActionResult Edit(EditStudentViewModel viewModel)
{
if (ModelState.IsValid)
{
Student student = studentRepository.Find(User.Identity.GetUserId());
if (student == null)
{
var newStudent = new Student
{
AspNetUserRefId = viewModel.AspNetUserRefId,
CatchPhrase = viewModel.CatchPhrase,
StartedPracticing = Convert.ToInt16(viewModel.SelectedYearId),
LocationPoints = Geocoding.GetDbGeography(viewModel.Location),
Location = viewModel.Location,
SO I'm running through this method twice just to insert/update a student. It seems a little redundant.
Isn't there a way to trigger/set validation state while the code in the controller is running, so I don't have to call this method twice (once for validation and once to set the actual values) when the user submits the form?
I thought about caching but don't think it's a good idea, unless someone can point out something.
If you think applying validation upfront using an attribute on the text box serve value to the user (early feedback), keep things as it is. Two calls is not too bad at all considering the value and cleanliness of the solution.
Second option is you can remove the attribute, and perform the validation in the controller action. If validation fails, display same form with all the same data but error message for the text box value (location). User will need to choose another location and then submit.
It is a trade off.
Important Tip: You can optimize your solution by storing region names in your DB and going to google API only if the region name does not present in your DB.
Related
I am trying to update a custom field on the SOLine (UsrPOPromisedDate) when the POLine promised date is changed. Below is my graph extension, however SOLine is always null.
When I convert the BQL to T-SQL, I get the expected results returned. Why is my view always returning a null value?
using PX.Data;
using PX.KWW.MyProject.DAC;
using PX.Objects.PO;
using PX.Objects.SO;
namespace MyProject.Graph
{
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
public PXSelectJoin<
SOLine,
InnerJoin<SOLineSplit,
On<SOLineSplit.orderType, Equal<SOLine.orderType>,
And<SOLineSplit.orderNbr, Equal<SOLine.orderNbr>,
And<SOLineSplit.lineNbr, Equal<SOLine.lineNbr>>>>>,
Where<SOLineSplit.pOType, Equal<Current<POLine.orderType>>,
And<SOLineSplit.pONbr, Equal<Current<POLine.orderNbr>>,
And<SOLineSplit.pOLineNbr, Equal<Current<POLine.lineNbr>>>>>>
SalesOrderLine;
protected virtual void _(Events.FieldUpdated<POLine, POLine.promisedDate> eventHandler, PXFieldUpdated baseHandler)
{
baseHandler?.Invoke(eventHandler.Cache, eventHandler.Args);
POLine pOLine = eventHandler.Row;
if (pOLine is null) return;
SOLine sOLine = SalesOrderLine.Current;
SOLineExtension sOLineExtension = sOLine.GetExtension<SOLineExtension>();
if (sOLine is null || sOLineExtension is null) return;
sOLineExtension.UsrPOPromisedDate = pOLine.PromisedDate;
SalesOrderLine.Update(sOLine);
}
}
}
If you mean SalesOrderLine.Current is null, I don't believe the Current field is populated by default unless the user has selected a specific record within the view(or you manually set it).
If you are just trying to get the records within the view you would need to use SalesOrderLine.Select().
foreach(SOLine line in SalesOrderLine.Select()){
//Do Something Here
}
Looks like a mistake in your code.
And<SOLineSplit.pONbr, Equal<Current<POLine.orderType>>,
So I've got the following lines of code:
else
{
//if not found, call Gateway Add()
user.Id = await C3SDbContext.UserGateway.NextIdAsync(context);
user.CreatedById = modifier.CreatedById;
user.CreatedBy = modifier.CreatedBy;
user.DateCreated = DateTime.Now;
user.UserType = "G";
System.Diagnostics.Debug.WriteLine(user.UserType);
user.Status = UserStatus.NEW;
System.Diagnostics.Debug.WriteLine(user.UserType);
user.Uic = await C3SDbContext.UicGateway.GetUicByIdAsync(context, user.UicId);
System.Diagnostics.Debug.WriteLine(user.UserType);
user.Role = await C3SDbContext.RoleGateway.GetRoleByIdAsync(context, user.RoleId);
System.Diagnostics.Debug.WriteLine(user.UserType);
if (ModelState.IsValid)
{
userCheck = await C3SDbContext.UserGateway.AddNewGovernmentUserAsync(context, user, modifier);
}
else
{
System.Diagnostics.Debug.WriteLine(user.UserType);
ICollection<ModelState> ListValues = ModelState.Values;
List<object> Errors = new List<object>();
foreach (var item in ModelState.Values)
{
if (item.Errors.Count() > 0)
{
Errors.Add(item.Errors);
}
}
}
}
user is an instance of GovernmentUser.cs, which inherits from User.cs. UserType is a string property of User. When I run this, all the instances of "System.Diagnostic.Debug.WriteLine(user.UserType);" return "G" in the Output window. Heowever, the Errors list returns one item, telling me that UserType is null.
My questions are: what is going on here? How can they both come to different results, when executed at the same type, and how can I get it so that ModelState.IsValid == true?
ModelState checks the data that was posted to you in MVC. I don't know if this is in your controller or not, but if it is, then my guess is that the UserType wouldn't be on the original data that was posted. I don't think you can use that check after setting it server-side. It is a check on the data received from the client. If UserType isn't required on the client side, just remove that rule from the Dto. However, if you are uploading the actual Entity directly here and it is using the Required attribute that EF uses, then I would just save it like normal and let EF handle the valdiation instead of using ModelState.
public class MusicController : Controller
{
User currentUser;
public PartialViewResult UploadMusic()
{
return PartialView("_UploadMusic");
}
[HttpPost]
public ActionResult UploadMusic(List<HttpPostedFileBase> files)
{
EntityDBContext db = new EntityDBContext();
List<Song> uploadedSongs = new List<Song>();
foreach (var file in files)
{
if (file != null)
{
string songName = Path.GetFileName(file.FileName);
byte[] songAsBytes = new byte[file.ContentLength];
using (BinaryReader br = new BinaryReader(file.InputStream))
{
songAsBytes = br.ReadBytes(file.ContentLength);
}
//Save new record in database
Song song = new Song
{
SongName = songName,
SongBytes = songAsBytes
};
uploadedSongs.Add(song);
}
}
string userName = User.Identity.Name;
currentUser = db.Users.Where(x => x.Username == userName).First();
currentUser.UserSongs = uploadedSongs;
return ShowSongs(currentUser.UserSongs);
}
public ActionResult ShowSongs(List<Song> UserSongs)
{
return View("ShowSongs", UserSongs);
}
public ActionResult Publish()
{
EntityDBContext db = new EntityDBContext();
foreach (var song in currentUser.UserSongs)
{
if (song != null)
{
db.Songs.Add(song);
db.SaveChanges();
}
}
return View();
}
}
ShowSongs view:
#model List<Vidafo.Models.Song>
#Html.ActionLink("Publish", "Publish")
The Problem
So I declare currentUser at the top of the controller. I then assign a value to that with this line here currentUser.UserSongs = uploadedSongs; This works fine but when the code goes into Publish() currentUser.UserSongs is null.
I need to have access to currentUser.UserSongs in more than one action method after assigning a value but it seems that it resets to null when it enters another action.
Object state isn't maintained across requests, that's not how web applications work. Every time a request is sent to the server, a new instance of the controller object is created. So any instance-level values are new.
In order to persist information across requests you need to persist it somewhere. For something like a user context, session state is a common choice. You'll probably want to wrap it in a common provider interface so as to not couple your controllers to an HTTP context, but at its core storing in session is simple:
HttpContext.Current.Session["someKey"] = someValue;
(You could even re-fetch from the database with each request. It's slightly less performant, but very simple and robust.)
Don't count out the ASP.NET identity system for this, though. ASP.NET is pretty good at abstracting a lot of this for you. You're already using it here:
string userName = User.Identity.Name;
Then you use that value to get the user from the database. You could extend the identity system to store a custom user object which fits your needs. But that's a larger scope effort outside of this question.
For this you can make use of TempData i.e. store value in TempData dictionary. One problem here is MVC doesn't sore value of variable during postback i.e. during different action of same controller or calling another controller for this you can use temporary varialble TempData as suggested.
We're creating a WPF app in which we execute python scripts from different Test Stations and show the output in its corresponding output panel, To run the scripts in parallel we are using Task but when we run the scripts in parallel from the stations, We are getting the output of other stations also into the station that is started first, we're using the following code,
private void ZmqStatusListener(string endPoint)
{
using (Context context = new Context())
{
StatusPort = string.Empty;
TestResultPort = string.Empty;
using (Socket server = context.Socket(SocketType.REP))
{
try
{
if (isStatusContextActive == false || isPortChanged == true)
{
server.Bind(endPoint);
isStatusContextActive = true;
}
}
catch (ZMQ.Exception ex)
{
if (ex.Errno != 100)
{
string IPCPort = _globalParameters.GlbParam.GlbParamIpcStartPort;
if (IPCPort == string.Empty)
{
IPCPort = "0";
}
if (endPoint == EditorConstants.PortAddress.PortPrefix + IPCPort)
{
StatusPort = endPoint;
TestReultError = EditorConstants.CommonMessageTypes.TestReultError + ex.Message + EditorConstants.CommonMessageTypes.StackTraceMessage + ex.StackTrace;
}
StopExecOfScript(default(object));
isCancelledtask = true;
ScriptStatusDesc = new ScriptStatusDesc()
{
Status = "Failed",
statusDescription = "Failed"
};
}
}
while (true)
{
string message = server.Recv(Encoding.UTF8);
UpdateTestResults(message);
server.Send(" ACK", Encoding.UTF8);
// if (message == "Test Passed")
//break;
}
}
}
}
and for testing purpose we're breaking the while loop in this code based on a test message we kept in the python script, then we are able to get the output in the respective station correctly but this way we can only run in a synchronous fashion which we don't want as we require to run the test stations in parallel and the while loop should not break as it should be listening for the response.
We were able to solve the issue by getting clues doing a sample app to reproduce the issue and to first know whether our ClrZmq pattern was correct for us or not and it is correct. The resolution we followed is that when we needed to bind that data to its corresponding View's Model object in its ViewModel so had to retrieve View's DataContext which is of Type ISomeXViewModel for the particular TestStation using an Id of that TestStation we did this cos all of our TestStations are dynamically added and we even store it to be accessed wherever necessary. This issue was caused to due multiple instances of UserControls so we explicitly needed to update the TestStation manually with a little more effort.
Sample Code Snippet
private void BindTestResult(string xmlPayLoad)
{
// converting xmlPalLoad to a class/model object
ITestStationViewModel viewModel = (ITestStationViewModel)((IView)DynamicTestStationsGrid.Children[StationNumber].Content).DataContext;
// IView class has DataContext property so I am type casting the Content which is ContentControl to IView type first and later to ITestStationViewModel
viewModel.TestStationModel = xmlPayLoadModel;
}
Thanks.
I'm tryping to use JSON to update records in a database without a postback and I'm having trouble implementing it. This is my first time doing this so I would appreciate being pointed in the right direction.
(Explanation, irrelevant to my question: I am displaying a list of items that are sortable using a jquery plugin. The text of the items can be edited too. When people click submit I want their records to be updated. Functionality will be very similar to this.).
This javascript function creates an array of the objects. I just don't know what to do with them afterwards. It is called by the button's onClick event.
function SaveLinks() {
var list = document.getElementById('sortable1');
var links = [];
for (var i = 0; i < list.childNodes.length; i++) {
var link = {};
link.id = list.childNodes[i].childNodes[0].innerText;
link.title = list.childNodes[i].childNodes[1].innerText;
link.description = list.childNodes[i].childNodes[2].innerText;
link.url = list.childNodes[i].childNodes[3].innerText;
links.push(link);
}
//This is where I don't know what to do with my array.
}
I am trying to get this to call an update method that will persist the information to the database. Here is my codebehind function that will be called from the javascript.
public void SaveList(object o )
{
//cast and process, I assume
}
Any help is appreciated!
I have recently done this. I'm using MVC though it shouldn't be too different.
It's not vital but I find it helpful to create the contracts in JS on the client side and in C# on the server side so you can be sure of your interface.
Here's a bit of sample Javascript (with the jQuery library):
var item = new Item();
item.id = 1;
item.name = 2;
$.post("Item/Save", $.toJSON(item), function(data, testStatus) {
/*User can be notified that the item was saved successfully*/
window.location.reload();
}, "text");
In the above case I am expecting text back from the server but this can be XML, HTML or more JSON.
The server code is something like this:
public ActionResult Save()
{
string json = Request.Form[0];
var serializer = new DataContractJsonSerializer(typeof(JsonItem));
var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(json));
JsonItem item = (JsonItem)serializer.ReadObject(memoryStream);
memoryStream.Close();
SaveItem(item);
return Content("success");
}
Hope this makes sense.
You don't use CodeBehind for this, you use a new action.
Your action will take an argument which can be materialized from your posted data (which, in your case, is a JavaScript object, not JSON). So you'll need a type like:
public class Link
{
public int? Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Url { get; set; }
}
Note the nullable int. If you have non-nullable types in your edit models, binding will fail if the user does not submit a value for that property. Using nullable types allows you to detect the null in your controller and give the user an informative message instead of just returning null for the whole model.
Now you add an action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DoStuff(IEnumerable<Link> saveList)
{
Repository.SaveLinks(saveList);
return Json(true);
}
Change your JS object to a form that MVC's DefaultModelBinder will understand:
var links = {};
for (var i = 0; i < list.childNodes.length; i++) {
links["id[" + i + "]"] = list.childNodes[i].childNodes[0].innerText;
links["title[" + i + "]"] = list.childNodes[i].childNodes[1].innerText;
links["description[" + i + "]"] = list.childNodes[i].childNodes[2].innerText;
links["url[" + i + "]"] = list.childNodes[i].childNodes[3].innerText;
}
Finally, call the action in your JS:
//This is where I don't know what to do with my array. Now you do!
// presumes jQuery -- this is much easier with jQuery
$.post("/path/to/DoStuff", links, function() {
// success!
},
'json');
Unfortunately, JavaScript does not have a built-in function for serializing a structure to JSON. So if you want to POST some JSON in an Ajax query, you'll either have to munge the string yourself or use a third-party serializer. (jQuery has a a plugin or two that does it, for example.)
That said, you usually don't need to send JSON to the HTTP server to process it. You can simply use an Ajax POST request and encode the form the usual way (application/x-www-form-urlencoded).
You can't send structured data like nested arrays this way, but you might be able to get away with naming the fields in your links structure with a counter. (links.id_1, links.id_2, etc.)
If you do that, then with something like jQuery it's as simple as
jQuery.post( '/foo/yourapp', links, function() { alert 'posted stuff' } );
Then you would have to restructure the data on the server side.