Initializing class member in UserControl - c#

Class is derived from Control in Asp.Net C#.
Public class member is defined as:
public bool isPresent = true;
Then in Render method check is performed :
if (isPresent)
doSomething;
On a form this field is set to false:
<c:CustomControl id="CustomControl1" isPresent="false">
When this code is executed locally from VS, everything is fine. Being deployed to the server, however, throws exception for the line with check for "if (isPresent)", saying that object reference is not set.
At the same time, if this line is changed to be "if (isPresent == true)", everything is fine both locally and on the server.
Is setting of value for class member of the Control different when run from VS and from IIS? Is it initialized in IIS before comparison operation, and not before implicit check?
UPDATE: as has been correctly pointed, this variable is a field, not a property. There is no other class member (and no property with same name).
UPDATE2: in addition, if check for value being not null is added, there is no exception anymore. Can it be the case that object initializer sets value of the field in case of explicit comparison operation?
if (isPresent == null)
return;
if (isPresent)
doSomething;

When creating user controls, the most advisable manner to store property values is in the View State, as follows:
public bool IsPresent
{
get
{
bool isPresent = false;
if (ViewState["IsPresent"] != null)
{
isPresent = (bool) ViewState["IsPresent"];
}
return isPresent;
}
set
{
ViewState["IsPresent"] = value;
}
}
Then, the control would be declared as follows (Note that the IsPresent starts with Upper I):
<c:CustomControl id="CustomControl1" IsPresent="false">
Whenever storing data on controls, keep in mind that they must be persisted on the page. If you simply declare a variable, there is no guarantee that the data will hold between requests. The only manner to make sure the data is persisted, is to save it in the View State. You can find support to these statements here and here.
The question of why the behavior changes in the IIS and Visual Studio may be not relevant here because simply declaring a variable, as stated above, provides no assurance at all that the data will be saved.
Regarding the Property being a getter/setter, note that it must be declared as shown above, in order to the property to be recognized by the ASPX page.

Related

After renaming a parameter, runtime refers to old name, that does not exist anywhere in my code

I have a razor component. The component have a parameter, EventCallback to be precise, named "ValueChanged".
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
I refer to the event exactly in one razor file like this:
<Autocomplete DataSource="#Data" Columns="Id,Name" ValueChanged="OnUnitBrandIdChanged" />
I renamed (via F2, AKA refactor) the property name to "OnValueChanged". The razor file was not automatically updated (I guess a bug in Visual Studio). So I updated it manually. So now both points of interest looks like this:
[Parameter]
public EventCallback<string> OnValueChanged { get; set; }
and
<Autocomplete DataSource="#Data" Columns="Id,Name" OnValueChanged="OnUnitBrandIdChanged" />
The code compiles and runs, then the runtime crashes with following error message
Unhandled exception rendering component: Object of type 'Woof.Blazor.Components.Autocomplete' does not have a property matching the name 'ValueChanged'.
I wasted 4 hours investigating the case.
First, I changed the name "OnValueChanged" to "SomethingElse" - to make sure, the string "ValueChanged" doesn't exist in my source code at all.
Of course I get the same error message.
Then I searched ALL files in my project, including hidden and binary files for the string "ValueChanged". Of course I found the string in obj, bin and .vs directories. So I deleted those files.
After compiling the code again, I get the same error message. The one with 'ValueChanged' reference.
I created completely new Blazor project. I copied all my code files, triple-checking NONE of them contains "ValueChanged" string.
When the new project is run - I get the same error message.
I thought maybe Visual Studio has written the reference in a file outside the project directory in a hidden place. So I remove all temporary files from the project, published it on GitHub and send to my coworker. He cloned the project, run it and got the same error message.
I created the property with the name "ValueChanged", dummy property, object type. Completely unused and redundant. Of course program runs without errors.
It appears the reference to that name is hidden somewhere but I have no idea where. No Windows tool is able to search "ValueChanged" string within project directory. I even suspected the Visual Studio could hide the reference by encrypting and / or compressing the content, but again I triple-checked I removed ALL binary files, all non plain text files.
All for nothing.
Then I created completely new Blazor project. Created a test component, created an event, bound dummy event handler, compiled and run. Everything worked. Then I refactored my test program in the exact same way as the original one. It worked, no problems at all. I even used exactly the same names, types, directory structure, namespaces, I even added the parameter named "Value" to make my test case more similar to the production code. The result is the new project behaves normally. I can rename any parameter and it just works.
My old project doesn't behave normally even when it's almost rewritten from scratch WITHOUT the string "ValueChanged" occurring in any file ONCE. The same error message. Like with Visual Studio and Blazor the common logic no longer applies. The name doesn't exist, but when I compile the code, it suddenly appears in temporary files with g.cs extension.
It's most probably a horrific bug in Visual Studio / .NET Core, but to report it I should be able to reproduce it, but in this case - I can't. Any clues?
BTW, Of course I tried to debug it and set the breakpoint on the cursed property setter. It gets triggered, however the only items in my call stack are "External code", so it's completely useless.
Note the following:
You ordinarily define a parameter property in a component if this component is a child component which is bound to a parent component, in that case the parent component is bound to a Value property that should be defined in the child component, and decorated with the Parameter attribute as well. Thus, your child component should look like this
private string _value;
[Parameter]
public string Value
{
get { return _value ?? string.Empty; }
set
{
if (Value != value)
{
_value = value;
}
}
}
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
The following is the usage of the component in a parent component
<ChildComponent #bind-Value="value" />
#code
{
private string value;
}
Note: The code above (ParentComponent) embed the child component defined before, and bind the local variable called value to the Value property of the child component. When you want to bind to a property of a Component from a parent component you use the #bind directive + hyphen + the name of the property, as for instance: #bind-Password="password". In the bounded component (the child component) you should define the property (in the last instance it should be Password) and a delegate parameter property (in the last instance it should be
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
)
As you can see, we must have this pair. BUT ValueChanged or PasswordChanged are constructs which the compiler use to produce, behind the scene, code which enables communication between a parent component and a child component. To be more precise, the compiler produces code that enable two way data-binding between the components... You can't use ValueChanged or PasswordChanged as an attribute for event handlers. The only thing you can do is trigger the delegates. Thus you can put an input element in your child component, and bind the value attribute of the input element to the Value property of the component, like this:
<input type="text" value="#Value" #oninput="OnValueChanged" />
This is a one-way binding from the Value property of the child component to the value attribute of the element. We also have to update the Value property, in that case, whenever the user type a char... For this we need to define an event handler, which is called after each hit on the keyboard, like this:
private Task OnValueChanged(ChangeEventArgs e)
{
Value= e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
As you can see, the OnValueChanged method assign the value entered into the text box (this is done at each hit on the keyboard) into the Value property, and then.....................
triggers the ValueChanged delegate, passing it the value of the Value property. This is what you should do with ValueChanged, nothing else. Now, when the delegate is triggered, the value of the private value field in the parent component is updated with the value of the Value property of the child Component. This procedure is called two ways data-binding between Components

Sanitising ASP.NET input (allowing angle brackets)

I have read about customising input validation for ASP.NET requests to avoid the dreaded "a potentially dangerous value was detected". I'm using the following code to allow angled brackets to pass validation.
public class RequestValidator : System.Web.Util.RequestValidator {
protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) {
validationFailureIndex = 0;
switch (requestValidationSource) {
case RequestValidationSource.Form:
if (control is CustomTextBox) { // How can I get the control?
// allow angle brackets to pass validation
value = value.Replace("<", "<").Replace(">", ">");
}
break;
}
return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
}
}
Now that I'm allowing these potentially dangerous values through the validation filter I'd like to be sure that they're being properly handled. This is a Web Forms environment so I thought I'd create a CustomTextBox control, override the Text property and HtmlEncode the string.
As you can see from the comment in the code above, I'd like to restrict my RequestValidator so that it only allows 'dangerous' values to pass validation if I can be sure they're going to be handled by my CustomTextBox. How can I get a reference to the Control when all we have to go on is the value and the collectionKey?
The validation of the Form Keys happen even before the Page PreInit event. There are no controls created at that time.
I think your best chance would be holding to a collection of UniqueIDs of all CustomTextBox instances that exists on the page currently. Having that collection, you could check if the form key being validated exists on the collection, meaning that it is in fact a CustomTextBox.
This collection could be managed inside the CustomTextBox control and saved on the Session or Application Cache.
I would suggest to explicitly set ValidateRequestMode property to ValidateRequestMode.Disabled in your custom control constructor and override Text property (or whatever property or you are using to store value) getter and there return sanitized value based on your specific scenario.

How to debug a WPF application with asynchronous data access and no Call Stack?

I have a large WPF application that uses the MVVM design pattern and asynchronous data access methods. It uses the old style asynchronous code with callback handlers and the IAsyncResult interface... here is a typical example:
function.BeginInvoke(callBackMethod, asyncState);
Then , in the view model, I have the following callback handler:
private void GotData(GetDataOperationResult<Genres> result)
{
UiThreadManager.RunOnUiThread((Action)delegate
{
if (result.IsSuccess) DoSomething(result.ReturnValue);
else FeedbackManager.Add(result);
});
}
The RunOnUiThread method is basically the following:
public object RunOnUiThread(Delegate method)
{
return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}
This problem only affects one view model, the one that enables the users to edit the Release objects. On the related view, certain collections that populate ComboBoxes are requested from the database when it is first loaded. Let's simplify this saying that there is just one collection called Genres. After the data arrives in the view model, it is handled like so:
private void GotGenres(GetDataOperationResult<Genres> result)
{
UiThreadManager.RunOnUiThread((Action)delegate
{
if (result.IsSuccess) Genres.AddEmptyItemBefore(result.ReturnValue);
else FeedbackManager.Add(result);
});
}
When the collection is present and a Release object has been selected in the UI, I have the following code selects the current Release.Genre value from the collection:
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id))
Release.Genre = Genres.GetItemWithId(Release.Genre);
At this point, I should note that this all works fine and that this is the only line that references the Release.Genre property from the view model.
My particular problem is that sometimes the Release.Genre property is set to null and I can't work out how or from where. >> Edit >> When I put a break point on the property setter, << Edit << the Call Stack provides no real clues as to what is setting the null value, as there is only a [Native to Managed Transition] line. On selecting the Show External Code option from the Call Stack window, I can see basic asynchronous code calls:
Now I can confirm the following facts that I have discovered while attempting to fix this problem:
The one line that references the Release.Genre property is not setting it to null.
The call to Genres.AddEmptyItemBefore(result.ReturnValue) is not setting it to null... this just adds the result collection into the Genres collection after adding an 'empty' Genre.
The Release.Genre property is sometimes set to null in or after the call to Genres.AddEmptyItemBefore(result.ReturnValue), but not because of it... when stepping through it on a few occasions, execution has jumped (in an unrelated manner) to the break point I set on the Release.Genre property setter where the value input parameter is null, but this does not happen each time.
It generally happens when coming from a related view model to the Release view model, but not every time.
The related view model has no references to the Release.Genre property.
To be clear, I am not asking anyone to debug my problem from the sparse information that I have provided. Neither am I asking for advice on making asynchronous data calls. Instead, I am really trying to find out new ways of proceeding that I have not yet thought of. I understand that some code (almost certainly my code) somewhere is setting the property to null... my question is how can I detect where this code is? It does not appear to be in the Release view model. How can I continue to debug this problem with no more clues?
I usually use Flat File, XML or Database logging for debugging purpose. I created those Log classes for logging purpose, so that I can call it from every applications I develop.
For database logging, you can do it as simple as:
void WriteLog(string log){
// Your database insert here
}
Maybe you need datetime and other supporting information, but it's up to the developer. For simple flat file logging is:
void WriteLog(string log){
using(TextWriter tx = new StreamWriter("./Log/" + DateTime.Now.ToString() + ".txt", false)){
tx.WriteLine(log);
}
}
You can use the logging in your application in both ways:
1: Method call
WriteLog((Release.Genre == null).ToString());
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id))
Release.Genre = Genres.GetItemWithId(Release.Genre);
2: Add it in your Release.Genre set (or get) property
public class Release{
private Genre _genre=null;
public Genre Genre{
get{
WriteLog((_genre == null).ToString());
return _genre;
}
set{
WriteLog((_genre == null).ToString());
_genre = value;
}
}
}
With this, you can try to get the call sequence, whether the Release.Genre is being set in other places before, during call, etc.
Please note I just giving the general image of building logging. Please expect errors. However, it is developer's responsibility to develop the Logging acitivities to meet requirement.

Binding not refresing when I set the variable in the setter

I have a text bound to a property as follows
The user is expected to type in a File Name. Sometimes however users may type in invalid characters. So my backing property in the View Model looks as shown below
private string outputFileName;
public string OutputFileName
{
get
{
return outputFileName;
}
set
{
string temp = value;
if (true == IsValidFileName(temp))// this function uses Path.Getinvalidfilechars
{
outputFileName = value;
}
else
{
MessageBox.Show(string.Format("{0} contains one or more invalid characters
for a file Name",temp));
}
base.OnPropertyChanged("OutputFileName");
}
}
Here is the problem, the text box still shows the invalid char. why is the OnPropertyChanged not causing the text in the text box to go back to the old value without the invalid char.
How can I get that behaviour
In the else statement, the backing field for OutputFileName is not being assigned a different value. If you want to revert back to the previous value, then save that in another variable and update the backing field in the else statement and then the property changed event will change the UI with the old value. Although, I don't think this is very good user experience.
A better solution would to be use some validators and inform the user that the input needs to be corrected, rather than just reverting the value back to the previous value.
Google up "wpf validation" or start with this SO question: WPF Data Binding and Validation Rules Best Practices
What is the base class of your class that defines the OutputFileName? That class' OnPropertyChange method seems to check if the property value really changed before firing the PropertyChanged event. I tried your example with a class that directly implements INotifyPropertyChanged and does
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("OutputFileName"));
}
and that works as you expect. Although i agree with Marc, showing a MessageBox from a property setter is rather uncommon.

Validation errors prevent the property setter being called

I am looking for a simple solution to the following problem:
I am using a simple TextBox control with the Text property bound to a property in the code behind. Additionally I am using a validation rule to notify the user of malformed input.
... error display style here ...
Now after entering valid data into the TextBox the user can hit a button to send the data. When clicking the button the data from the bound property UserName in the code behind is evaluated and sent.
The problem is that a user can enter valid data into the TextBox and this will be set in the property UserName. If the user then decides to change the text in the TextBox and the data becomes invalid, the setter of the property UserName is not called after the failed validation.
This means that the last valid data remains in the property UserName, while the TextBox display the invalid data with the error indicator. If the user then clicks on the button to send the data, the last valid data will be sent instead of the current TextBox content.
I know I could deactivate the button if the data is invalid and in fact I do, but the method is called in the setter of UserName. And if that is not called after a failed validation the button stays enabled.
So the question is: How do I enable calling of the property setter after a failed validation?
You could set the ValidationRule.ValidationStep property for your validation rules to ValidationStep.UpdatedValue. This first updates the source, and then performs validation. That means, your property setter should be called even though your validation fails. Note that this property is only available from .NET 3.5 SP1 upwards. For more details, see this blog post (paragraph "How do I use it? (Part 1)").
How I handle this in my view model classes:
public class MyViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private Dictionary<string, string> _Errors = new Dictionary<string, string>();
public object SomeProperty
{
get { return _SomeProperty; }
set
{
if (value != _SomeProperty && !ValidationError("SomeProperty", value))
_SomeProperty = value;
OnPropertyChanged("SomeProperty");
}
}
}
private bool ValidationError(string propertyName, object value)
{
// I usually have a Dictionary<string, Func<object, string>> that maps property
// names to validation functions; the functions return null if the property
// is valid and an error message if not. You can embed the validation logic
// in the property setters, of course, but breaking them out as separate methods
// eases testing.
_Errors[propertyName] = _ValidationMethods[propertyName](value);
OnPropertyChanged("IsValid");
}
public bool IsValid
{
get { return !(_Errors.Where(x => x.Value != null).Any()));
}
public string this[string propertyName]
{
get
{
return (_Errors.ContainsKey(propertyName))
? _Errors[propertyName]
: null;
}
}
}
It's a little awkward to get this all set up at first, but once you've done it, you have a simple and straightforward way to report validation errors to the UI (via the DataErrorValidationRule), a straightforward way to know whether any given property is valid or not (check _Errors), and an IsValid property that tells you whether or not the whole view model is valid. (Also, you can extend the IsValid property to handle the case where all the properties of the view model are valid but the view model itself is not, e.g. two mutually exclusive flags are both set.) And as long as you make them internal, the validation methods can be unit tested via NUnit or whatever.
I should add that the above code is off the top of my head and may or may not work as written - my actual working code is in a base class and has a lot of other things baked into it that would just be confusing.

Categories

Resources