I have two assemblies in my application. MyApplication.BO and MyApplication.GUI.
I have configured property-settings for my BO assembly.
Now when I am trying to compile the following code:
public class MyApplicationInfo
{
private string _nameOfTheUser;
public string FullNameOfTheUser
{
get { return _nameOfTheUser; }
set { _nameOfTheUser = value; }
}
public void Save()
{
try
{
MyApplication.BO.Properties.Settings.Default.FullNameOfTheUser = this.FullNameOfTheUser;
MyApplication.BO.Properties.Settings.Default.Save();
}
catch (Exception ex)
{
throw ex;
}
}
}
VS2005 is giving me the following compilation error:
Error 1 Property or indexer 'MyApplication.BO.Properties.Settings.FullNameOfTheUser' cannot be assigned to -- it is read only F:\CS\MyApplication\MyApplication.BO\MyApplicationInfo.cs 57 17 MyApplication.BO
What is wrong with my approach?
In the Settings designer, make sure that the Scope property for FullNameOfTheUser is set to "User". If you create an Application-scoped setting, it is generated as a read-only property. Take a look at this article for more information.
The setting needs to have user, not application scope.
Related
I have a console app where I'm trying to bind a config section into a list of a custom object type as such:
List<myObject> myObjectList = new List<myObject>();
config.GetSection("Objects").Bind(myObjectList);
public enum CustomEnum{
One,
Two
}
public class myObject{
public CustomEnum myEnum { get; set; }
}
My issue is that if in the config file, you don't give a proper enum value for that property (ie Three instead of One or Two as shown above), it will fail to bind the object, and wont throw an error telling you that it failed. So basically how can I make it so I know a bind failed because of an improper config value? Right now it just doesn't bind those specific ones that fail, meaning there's no real way to know there are items missing.
Example config that successfully binds one object but fails and ignores the other:
{
"Objects": [
{
"myEnum": "One"
},
{
"myEnum": "Three"
}
]
}
EDIT: So for future reference for anyone finding this, there basically is no good solution. You just need a manual check, or to not use enums at all in your config.
Validate the object "By Hand" before binding the object.
Loop through the values of the config as string first. See if they match an existing item in the enum. Using the IsDefined function.
https://learn.microsoft.com/en-us/dotnet/api/system.enum.isdefined?view=netframework-4.8
If not throw an exception.
If all items are in the enum use the Bind method as usual.
public static bool ValidateConfig(object jsonConfig)
{
foreach (string item in jsonConfig) // loop through the config items as string
{
if (!Enum.IsDefined(typeof(CustomItem), item))
{
throw new Exception("Enum value does not exist.");
}
}
}
Based on this post:
The ASP.NET Core configuration system is very flexible and allows you
to use strongly typed settings. However, partly due to this
flexibility, it's possible to have configuration errors that only
appear in certain environments. By default, these errors will only be
discovered when your code attempts to use an invalid configuration
value (if at all).
You could use an IStartupFilter to validate your settings when your
app starts up. This ensures you learn about configuration errors as
soon as possible, instead of at run-time.
So at first you need to create an interface like this:
public interface IValidatable
{
void Validate();
}
And then:
public class SettingValidationStartupFilter : IStartupFilter
{
readonly IEnumerable<IValidatable> _validatableObjects;
public SettingValidationStartupFilter(IEnumerable<IValidatable> validatableObjects)
{
_validatableObjects = validatableObjects;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
foreach (var validatableObject in _validatableObjects)
{
validatableObject.Validate();
}
//don't alter the configuration
return next;
}
}
Then you need to register the filter with the DI container:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IStartupFilter, SettingValidationStartupFilter>()
// other service configuration
}
And finally, you need to implement the IValidatable interface on your settings that you want to validate at startup:
public class myObject : IValidatable
{
public CustomEnum myEnum { get; set; }
public void Validate()
{
//Do your validation here
Validator.ValidateObject(this, new ValidationContext(this), validateAllProperties: true);
}
}
Whichever approach you use, the Validate() method throws an exception if there is a problem with your configuration and binding.
I am trying to create a more robust method that returns two different object types depending on results.
If the result is negative then return CustomError object, but if the result is positive then return Auto object.
Example below to demonstrate.
AutoService.cs
public class AutoService {
public async Task<object> Create(NewAuto model)
{
var auto = new Auto {
Type = model.Type,
Year = model.Year,
// other parameters
}
try {
await _autoDb.Create(auto);
}
catch (Exception e) {
// return this error object if something broken
return new CustomError { Message = "It is broken" }
}
//return the Auto entity if successful
return auto;
}
}
CustomError.cs
public class CustomError {
public string Message {get;set;}
}
In the current format the when calling Create method i will need to cast the result which brings headaches of its own (cast against CustomError or Auto class for e.g.).
Any advice how i can do this properly?
Why not create a class to represent the result, something like:
class EntityResult<T> {
public EntityResult(T entity) {
Success = true;
Entity = entity;
}
public EntityResult(string error) {
Success = false;
Error = error;
}
bool Success {get; }
T Entity { get; }
string Error { get; }
}
Usage would be like:
public async Task<EntityResult<Auto>> Create(NewAuto model) {
var auto = new Auto {
Type = model.Type,
Year = model.Year,
// other parameters
};
try {
await _autoDb.Create(auto);
return new EntityResult(auto);
} catch (Exception e) {
// return this error object if something broken
return new EntityResult<Auto>("Error goes here");
}
}
I wouldn't use a return type to represent two different results e.g. success and failure. It will make the code hard to understand and, as time goes on, you will (probably) find that the return type will get abused and expanded to contain other (irrelevant?/unnecessary?) information.
Apply the Single Responsibility Principle to the return type:
If the call was successful then return the correct object i.e. Auto
If the call failed (i.e. you caught and exception) then create a custom exception to pass that failure back up the call stack. The name of your exception will make the code clearer than enclosing errors in a generic object.
Also your calling code will be much cleaner (and easier to maintain) if you use exception handling instead of an object with two purposes. Keep it simple.
I may be going about this incorrectly but this is my class that I wrap my entity object:
using System;
using System.Linq;
namespace SSS.ServicesConfig.data
{
public partial class GlobalSetting
{
private static GlobalSetting _globalSettings;
public static GlobalSetting GlobalSettings
{
get
{
if (_globalSettings == null)
{
GetGlobalSetting();
}
return _globalSettings;
}
}
private static void GetGlobalSetting()
{
try
{
using (var subEntities = PpsEntities.DefaultConnection())
{
_globalSettings = (from x in subEntities.GlobalSettings
select x).FirstOrDefault();
if (_globalSettings == null)
{
_globalSettings = new GlobalSetting();
_globalSettings.GlobalSettingId = Guid.NewGuid();
_globalSettings.CompanyCode = string.Empty;
_globalSettings.CorporationId = Guid.Empty;
_globalSettings.DefaultBranch = "01";
_globalSettings.SourceId = Guid.Empty;
_globalSettings.TokenId = Guid.Empty;
subEntities.AddToGlobalSettings(_globalSettings);
subEntities.SaveChanges();
}
}
}
catch (Exception ex)
{
Logging.Log("An error occurred.", "GetGlobalSetting", Apps.ServicesConfig, ex);
throw new Exception(string.Format("Unable to retrieve data: [{0}].", ex.Message));
}
}
internal static void SaveGlobalSettings()
{
using (var entities = PpsEntities.DefaultConnection())
{
entities.Attach(_globalSettings);
entities.SaveChanges();
}
}
}
}
I'm trying to make it where they have to go through my class to get the settings record and save it though the same class. This is in a separate project that several other projects are going to import.
My save isn't saving to the database and I see no errors or changes on the record. In this particular table, there is only one record so it's not adding another record either.
Any suggestions?
First your save is not being called after the initial value is assigned to _globalSettings.
Second You should not be trying to change the value with a get accessor. It is bad form.
http://msdn.microsoft.com/en-us/library/w86s7x04.aspx
I recommend that you separate the responsibility of the save to the database to a new method (you could expose the SaveGlobalSettings method by making it public), but if you are determined to obfuscate the save from the user, then I would recommend you remove the save to the database from get accessor of the GlobalSettings property, create a set accessor for the GlobalSettings property, and put the save to the database in the GlobalSettings properties set accessor.
One other note, you are killing your stack trace.
throw new Exception(string.Format("Unable to retrieve data: [{0}].", ex.Message));
You can still catch and log the exception the way that your are doing it, but re-throw the exception like this:
catch (Exception ex)
{
Logging.Log("An error occurred.", "GetGlobalSetting", Apps.ServicesConfig, ex);
throw;
}
This will preserve the original exception.
Working on a sideproject with WP8, but having trouble getting IsolatedStorage working. I have looked at dozens of posts seemingly asking the same question, but I haven't been able to get any of the solutions to work. The application is a simple task organizer where I have created my own Task Objects, one being a Summary Task and each SummaryTask containing a list of BasicTasks. I have tried using XMLSerializing only to run into problems because I was using an ObservableCollection. Thought I could change the collection to a Subclass of INotifyPropertyChanged but that didn't work either. Quite frankly, I'm still getting the hang of the different between the two anyways. So anyways, my latest attempt involves trying to use IsolatedStorage Settings and that didn't work either. Here is my class definition:
class SummaryTask : TaskItem
{
public List<BasicTask> children = new List<BasicTask>();
private string sumTaskName;
private int sumTaskId;
public SummaryTask()
{
}
public SummaryTask(string name, int id)
{
sumTaskName = name;
sumTaskId = id;
}
public string SumTaskName
{
get { return sumTaskName; }
set { sumTaskName = value; }
}
public int SumTaskId
{
get { return sumTaskId; }
set { sumTaskId = value; }
}
public void addTask(string taskName, string taskText, int taskId){
children.Add(new BasicTask(taskName, taskText, taskId));
}
public List<BasicTask> CHILDREN
{
get { return children; }
}
}
}
I create a list of this SummaryTask in a Global variable and use it throughout my pages for easy access. Here is what the beginning of my MainPage.xaml.cs file looks UPDATED:
public MainPage()
{
InitializeComponent();
BackKeyPress += OnBackKeyPressed;
if (Global.settings.Contains("list"))
{
Global.list = (List<SummaryTask>)Global.settings["list"];
}
else
{
Global.list = new List<SummaryTask>();
}
}
Guidance on the poor quality of my code and how to improve it is also accepted. Thank you.
Edit: The exception indicates that an item with the same key has already been created. The stacktrace doesn't show anything of importance in this case. I should also note that the exception is thrown after adding an object to the list and trying to save it, not while compiling.
The piece of code I am using to try to save to the Isolated Storage is here, it triggers when I navigate to MainPage.xaml:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
resultList.SelectedItem = null;
Global.settings["list"] = Global.list;
Global.settings.Save();
}
No exceptions anymore, but exiting the app and reentering isn't pulling up any saved data.
The problem with Add is very simple to fix - just use the indexer instead, which allows you to overwrite an entry with the same name:
settings["list"] = Global.list;
That won't fix the Save call... but you'd need to give more details about what exception (not just "it tells me", the full exception details) to help us help you more.
I have my own wcf service class which should as result return string with "Everything Save Saccesfully" if ok save data. Otherwise I need to return in this string exception.
string SaveData(Stream stream)
{
string error = "";
try
{
//do saving data
error+="Everything Save Saccefully";
return error;
}
catch(Exception ex)
{
throw ex;
}
finally
{
}
}
It is possible to catch errors occurs in try block and return it in error variable ?
Well, the traditional way that you express the error is simply via an exception - not a return value. Lack of exception should imply "everything went correctly". That's the normal idiomatic way of working.
Now, occasionally it's worth having a method which doesn't throw an exception, but instead returns a success status and potentially a separate result via an out parameter. But that's relatively rare - it's usually for things like parsing, where it's entirely normal for it to fail in hard-to-predict ways when nothing's really wrong other than the input data.
In your case, it looks like this should be a void method which simply throws an exception on error.
Normally I would do as suggested by Jon, but if you really don't want to allow any exceptions to bubble up from your service you could encapsulate your success and (any) failure error information in a class structure like this
public class ErrorInfo
{
public string Message {get; set;}
// TODO: Maybe add in other information about the error?
}
public class SaveResult
{
public bool Success { get; set; }
/// <summary>
/// Set to contain error information if Success = false
/// </summary>
public ErrorInfo ErrorInfo { get; set; }
}
And then...
public SaveResult SaveData(Stream stream)
{
SaveResult saveResult = new SaveResult();
string error = "";
try
{
//do saving data
saveResult.Success = true;
}
catch (Exception ex)
{
saveResult.ErrorInfo = new ErrorInfo { Message = ex.Message };
}
}
The downside of this approach is that your caller must check the Success value after calling SaveData. Failure to do so can result in a bug in your code that will only manifest itself if the save fails.
Alternatively, if you don't handle the exception you get to benefit from one of the useful things about structured exception handling: If the caller forgets to explicitly handle any exception then it will bubble up the call stack and either crash the app or get caught by some higher-level error handling code.
Not necessarily ideal, but generally better than your code silently assuming that something succeeded, when in fact it didn't.
You should use Faults for exceptional results in WCF. I do not see idea why you would need exception in string. But if you absolutely need this, just move return statement to the end and set its value in catch block too.
string SaveData(Stream stream)
{
string error = "";
try
{
//do saving data
error+="Everything Save Saccefully";
}
catch(Exception ex)
{
//throw ex; Do not throw, just return its string representation
error+=ex.ToString();
}
finally
{
}
return error;
}
Try this :
string SaveData(Stream stream)
{
string error;
try
{
//do saving data
error ="Everything saved Successfully";
}
catch(Exception ex)
{
error = ex.Message;
}
return error;
}