Here is my code to add a viewstate key:
if (ViewState[params[i].Name] != null || true){
if (ViewState[params[i].Name] == null) //create ViewState item
ViewState[params[i].Name] = "SomeValue"; //...}
On the first Callback, the code runs and the ViewState item is added. However, on the second callback, when the first IF statement is hit to test for the ViewState item being there, it is not, it is NULL!!
ViewState is NOT DISABLED anywhere on my pages or controls. I thought the code above was the correct way to create a viewstate item to persist across multiple callbacks??
Thanks!
ViewState isn't handle for default on callbacks that why you got there null.
You can handle this for your own but it isn't easy that for sure.
Static field isn't a good aproach for any of that kind issues. It need to get unique key for every user in application if not it's will make hard to understand behaviour in the future.
Hope my answer make some light on this:)
Answering my own question.
I ended up creating a static dictionary for the info I needed. Does the job just fine.
Related
So, I thought that I could add a new element to the user Session to add some functionality.
I honestly thought I could do this:
SomeFunction(param1, NEWparam)
{
Session("MyNewParam") = NEWparam;
//So this would create a new session element called 'MyNewParam', right..?
...
}
That gets called when the user presses a button and then another webpage loads up.
The result, with this new line of code: The next web page doesn't load. Nothing happens.
Any and all comments are welcomed.
Solutions or helpful comments would be great.
Your syntax is right. However, I've run into an issue before where Classic ASP didn't want to take a session variable if it wasn't explicitly typed though. I'm not sure why this is, but it's worked for me in the past.
Session("MyNewParam") = parseInt(NEWparam);
Obviously, you could use String(), parseFloat()... or whatever. As I said, the syntax is right otherwise, so if the code isn't working you may want to start looking at other parts of the function that might be causing the problem.
I have a problem, which I'm really trying to figure out how I could best solve this. I have read various posts regarding this error and seems you can avoid this by using JavaScript by using:
Xrm.Page.getAttribute("name").setSubmitMode("always");
which doesn't work for me or inside the plugin. Now to my problem, I have an update plugin firing on my incident form, which updates some fields. However when I try to resolve the case or cancel it I get the error from my update plugin "The object cannot be updated because it is read-only" I have tried the following and would appreciate if somone could advice me what I'm doing wrong. My code for a plugin I'm registering as SetState on pre-operation against the incident form:
SetStateRequest setState = new SetStateRequest();
setState.EntityMoniker = new EntityReference();
setState.EntityMoniker.Id = incidentId; //Id which needs to be resolved/canceld
setState.EntityMoniker.Name = "statecode";
setState.EntityMoniker.LogicalName = "incident";
setState.State = new OptionSetValue();
setState.Status = new OptionSetValue();
SetStateResponse setStateResponse = (SetStateResponse)service.Execute(setState); }
On the state and status I'm quite confused to what value I have to set it to. I'm just getting an error when my incident is on Active and I'm trying to resolve and cancel the case. I would appreciate if someone could help me out here. Thanks in advance.
I think there are a few areas of confusion in your post...
Xrm.Page.getAttribute("name").setSubmitMode("always");
This is clientside code and will never have any bearing on the behaviour of your (serverside) plugin. It merely forces an attribute on the form to be submitted whether it has changed or not, during a save. If the record is in a read-only state, it will not change that fact.
I'm not at all clear what you are trying to acheive in your code. You mention that an update plugin is failing; you have posted code which would attempt to set the state of the incident to something (as #glosrob suggests, you are not providing any values in the OptionSetValue objects for State and Status so as you might already know, the code you have posted is invalid); you then state that you have registered your plugin on the SetState request. This means that it would fire if the user tries to set the state of the incident. Given that your code is itself trying to set the status of the incident, I'm not sure that it makes sense...
It sounds like what you want to do is, on update of an incident, set certain values. If the incident is in a read-only state, make it readable first, and then update the values. Do you then need to restore the state of the entity to it's former state? It sounds awkward and might perhaps suggest that there is a better way to meet your core requirement.
Maybe start with what you are trying to achieve and we can work from there :)
You should remove
setState.EntityMoniker.Name = "statecode";
from your code. This field Name has other purpose.
Also, you should add
setState.State.Value = 1;
setState.Status.Value = -1;
In the last three days I've struggled trying to find a way to accomplish what I though was supposed to be a simple thing. Doing this on my own or searching for a solution in the web, didn't help. Maybe because I'm not even sure what to look for, when I do my researches.
I'll try to explain as much as I can here: maybe someone will be able to help me.
I won't say how I'm doing it, because I've tried to do it in many ways and none of them worked for different reasons: I prefer to see a fresh advice from you.
In most of the pages of web application, I have two links (but they could be more) like that:
Option A
Option B
This is partial view, retured by a controller action.
User can select or both (all) values, but they can't never select none of them: meaning that at least one must be always selected.
These links must che accessible in almost all pages and they are not supposed to redirect to a different page, but only to store this information somewhere, to be reused when action needs to filter returned contents: a place always accessible, regarding the current controller, action or user (including non authenticated users) (session? cookie?).
This information is used to filter displayed contents in the whole web application.
So, the problem is not how to create the business logi of that, but how (and where) to store this information:
without messing with the querystring (means: keeps the querystring as empty/clean as possible)
without redirecting to other pages (user must get the current page, just with different contents)
allow this information to persists between all views, until user click again to change the option(s)
My aim is to have this information stored in a model that will contains all options and their selection status (on/off), so the appropriates PartialView will know how to display them.
Also, I could send this model to the "thing" that will handle option changes.
Thanks.
UPDATE
Following Paul's advice, I've took the Session way:
private List<OptionSelectionModel> _userOptionPreferences;
protected List<OptionSelectionModel> UserOptionPreferences
{
get
{
if (Session["UserOptionPreferences"] == null)
{
_userOptionPreferences= Lib.Options.GetOptionSelectionModelList();
}
else
{
_userOptionPreferences= Session["UserOptionPreferences"].ToString().Deserialize<List<OptionSelectionModel>>();
}
if (_userOptionPreferences.Where(g => g.Selected).Count() == 0)
{
foreach (var userOptionPreferencesin _userOptionPreferences)
{
userOptionPreferences.Selected = true;
}
}
UserOptionPreferences= _userOptionPreferences;
return _userOptionPreferences;
}
private set
{
_userOptionPreferences= value;
Session["UserOptionPreferences"] = _userOptionPreferences.SerializeObject();
}
}
Following this, I've overridden (not sure is the right conjugation of "to override" :) OnActionExecuting():
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
GetOptionSelections();
base.OnActionExecuting(filterContext);
}
GetOptionSelections()...
private void GetOptionSelections()
{
if (String.IsNullOrEmpty(Request["optionCode"])) return;
var newOptionCode = Request["optionCode "];
foreach (var userOptionPreferencesin UserOptionPreferences)
{
if (userOptionPreferences.OptionCode == newOptionCode )
userOptionPreferences.Selected = !userOptionPreferences.Selected;
}
}
This code I think can be better, but right now I just want to make it work and it doesn't.
Maybe there are also other issues there (quite sure, actually), but I believe the main issue is that OnActionExecuting is called by each action in a controller that inherit from BaseController, therefore it keeps toggling userOptionPreferences.Selected on/off, but I don't know how to make GetOptionSelections() being called only once in each View: something like the old Page_Load, but for MVC.
Last update AKA solution
Ok, using the session way, I've managed to store this information.
The other issue wasn't really on topic with this question and I've managed to solve it creating a new action that take cares of handling the option's change, then redirects to the caller URL (using the usual returnUrl parameter, but as action parameter).
This way, the option change is done only once per call.
The only thing I don't really like is that I can't simply work with the UserOptionPreferences property, as it doesn't change the session value, but only the value in memory, so I have to set the property with the new object's status each time: not a big deal, but not nice either.
This is a place to use session.
The session will keep your setting between requests while keeping it out of the url querystring. It seems that you have probably tried this already, but try it again and if you have problems ask again. I think it will be the best way for you to solve this problem.
I recently had a problem with multiple form posting in an ASP.NET MVC application. The situation was basically, if someone intentionally hammered the submit button, they could force data to be posted multiple times despite validation logic (both server and client side) that was intended to prohibit this. This occurred because their posts would go through before the Transaction.Commit() method could run on the initial request (this is all done in nHibernate)
The MVC ActionMethod looked kind of like this..
public ActionResult Create(ViewModelObject model)
{
if(ModelState.IsValid)
{
// ...
var member = membershipRepository.GetMember(User.Identity.Name);
// do stuff with member
// update member
}
}
There were a lot of solutions proposed, but I found the C# lock statement, and gave it a try, so I altered my code to look like this...
public ActionResult Create(ViewModelObject model)
{
if(ModelState.IsValid)
{
// ...
var member = membershipRepository.GetMember(User.Identity.Name);
lock(member) {
// do stuff with member
// update member
}
}
}
It worked! None of my testers can reproduce the bug, anymore! We've been hammering away at it for over a day and no one can find any flaw. But I'm not all that experienced with this keyword. I looked it up again to get clarification...
The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement, and then releasing the lock
Okay, that makes sense. Here is my question.
This was too easy
This solution seemed simple, straightforward, clear, efficient, and clean. It was way too simple. I know better than to think something that complicated has that simple a solution. So I wanted to ask more experienced programmers ...
Is there something bad going on I should be aware of?
No it's not that easy. Locking only works if the same instance is used.
This will not work:
public IActionResult Submit(MyModel model)
{
lock (model)
{
//will not block since each post generates it's own instance
}
}
Your example could work. It all depends on if second-level caching is enabled in nhibernate (and thus returning the same user instance). Note that it will not prevent anything from being posted to the database, just that each post will be saved in sequence.
Update
Another solution would be to add return false; to the submit button when it's being pressed. it will prevent the button from submitting the form multiple times.
Here is a jquery script that will fix the problem for you (it will go through all submit buttons and make sure that they will only submit once)
$(document).ready(function(){
$(':submit').click(function() {
var $this = $(this);
if ($this.hasClass('clicked')) {
alert('You have already clicked on submit, please be patient..');
return false;
}
$this.addClass('clicked');
});
});
Add it do you layout or to a javascript file.
Update2
Note that the jquery code works in most cases, but remember that any user with a little bit of programming knowledge can use for instance HttpWebRequest to spam POSTs to your web server. It's not likely, but it could happen. The point I'm making is that you should not rely on client side code to handle problems since they can be circumvented.
Yeah, it's that easy, but - there may be a performance hit. Remember that a Monitor lock restricts that code to be run by only one thread at a time. There is a new thread for each HTTP Request, so that means only one of those requests at any given time can access that code. If it's a long running procedure, or a lot of people are trying to access that part of the site at the same time - you might start to sluggish responses.
It's that easy, but be careful what object you lock on. It should be the same one for all the threads - for example, it could be a static object.
lock is syntactic sugar for a Monitor, so there is quite a bit going on under the cover.
Also, you should keep an eye out for deadlocks - they can happen when you lock on two or more objects.
I've started bumping into errors when my session has been lost, or upon rebuilding my project, as my forms authentication cookie still lives on.
In WebForms I'd use the masterpage associated with pages which require login to simply check for the session.
How would I do this in one location in MVC ? I'd hate having to check for session state in every action in my controllers.
On the other hand I can't just apply a global filter either, since not all Controllers require session state.
Would it perhaps be possible in my layout view ? It's the only thing the pages which require session have in common.
One thing you could do is to sub-class the controllers that do need session state. This way you could create a filter on just this base controller. This would allow you to do it all in one place. Plus, as you pointed out, a global filter won't help you here since the logic does not apply to every controller.
add it to session start. if a session loss happens it needs to trigger a session start too. you can handle it in there as follows:
protected void Session_Start(object src, EventArgs e)
{
if (Context.Session != null)
{
if (Context.Session.IsNewSession)
{
string sCookieHeader = Request.Headers["Cookie"];
if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
{
// how to simulate it ???
// RedirectToAction(“ActionName”, “ControllerName”, route values);
Response.Redirect("/Home/TestAction");
}
}
}
}
I agree with what Steve has mentioned, but I suggest to use Global Filters instead of creating a base class for all your controllers. The reason for this is everytime you create a new controller, you should always remember to derive from the base controller or you may experience random behaviours in your application that may take you hours of debugging. This is especially important when you stop development for a while and then get back to it.
Also, another reason is the "Favour composition over inheritance" principle.