class that wraps my entity object is not saving - c#

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.

Related

Ensure blocking of database rows during the Entity Framework request

I have created service which communicates with my database. GetAvailableUserId service's method cannot be run simultaneously, because I don't want to return same user's id for two different calls. So far I have managed this:
public class UserService : IUserService
{
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
transaction.Commit();
return availableUser.Id;
}
}
}
}
I wanted to test if service will work as intended, so I created simple console application to simulate synchronous requests:
Parallel.For(1, 100, (i, state) => {
var service = new UserServiceReference.UserServiceClient();
var id = service.GetAvailableUserId();
});
Unfortunately, It failed that simple test. I can see, that it returned same id for different for iterations.
Whats wrong there?
If I understood you correctly, you wan to lock method from other threads. If yesm then use lock:
static object lockObject = new object();
public class UserService : IUserService
{
public int GetAvailableUserId()
{
lock(lockObject )
{
// your code is omitted for the brevity
}
}
}
You need to spend some time and delve into the intricadies of SQL Server and EntityFramework.
Basically:
You need a database connection that handles repeatable results (which is a database connection string setting).
You need to wrap the interactions in EntityFramework within one transaction so that multiple instances do not possibly return the same result in the query and then make problems in the save.
Alternative method to achieve this is to catch DbUpdateConcurrencyException to check whether values in the row have changed since retrieving when you try to save.
So if e.g. the same record is retrieved twice. The first one to have the Available value updated in the database will cause the other one to thow concurrency exception when it tries to save because the value has changed since it was retrieved.
Microsoft - handling Concurrency Conflicts.
Add ConcurrencyCheck attribute above the Available property in your entity.
[ConcurrencyCheck]
public bool Available{ get; set; }
Then:
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
try
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
return availableUser.Id;
}
catch (DbUpdateConcurrencyException)
{
//If same row was already retrieved and updated to false, do not save, instead call the method again to get the next true row.
return GetAvailableUserId();
}
}
}

Handling errors and success in same format

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.

Bubbling up Update Concurrency Exceptions to view

What is a good way to bubble up a DbUpdateConcurrencyException to the view from the grain?
I'm currently working on an Orlean's prototype that has a custom state that I'm using Entity Framework Core to communicate with the DB and using the optimistic concurrency patterns built into EF Core to manage the concurrency issues.
Where I'm having an issue is that I want to bubble up my Exception from the grain to the view and am not receiving it on the view end.
I'm trying to accomplish this because I want to deal with some of the concurrency issues that are more pressing on the view so that the user can decide or at least be alerted to the issue.
I brought this up on the Orlean's Gitter, but didn't get many ideas from it.
Example of my code for updating:
public Task UpdateUser(User user)
{
//Reason for second try/catch is to bubble the error to controller
try
{
userState = new User
{
Username = this.GetPrimaryKeyString(),
accountType = user.accountType,
FName = user.FName,
LName = user.LName,
RowVersion = user.RowVersion,
CreatedDate = user.CreatedDate
};
UpdateState();
}
catch (DbUpdateConcurrencyException ex)
{
throw ex;
}
return Task.CompletedTask;
}
public Task UpdateState()
{
using (var context = new OrleansContext())
{
context.users.Update(userState);
try
{
context.SaveChanges();
}
catch ( DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var clientValues = (User)entry.Entity;
var databaseEntry = entry.GetDatabaseValues();
//Make sure the row wasn't deleted
if(databaseEntry != null)
{
var databaseValues = (User)databaseEntry.ToObject();
if(clientValues.accountType != databaseValues.accountType)
{
//Bubble up the exception to controller for proper handling
throw ex;
}
//Update Row Version to allow update
userState.RowVersion = databaseValues.RowVersion;
context.SaveChanges();
}
}
}
return Task.CompletedTask;
}
I'm open to any suggestions on this as long as it allows the user to be alerted to the Exception and can view their data and the current DB values.
There is a chance that the exception is not being serialized or deserialized correctly. The primary reasons for this could be:
The Exception class does not correctly implement the ISerializable pattern.
The assembly which contains the Exception class is not present on the client, so the client does not understand how to create the Exception type.
In this case, I would lean towards the second reason, because most (but not all!) Exception classes do correctly implement the ISerializable pattern.
In either case, you can catch your exception and turn it into a generic exception.
You could create a helper method to do this using the LogFormatter.PrintException(Exception) method from Orleans to format the exception as a string.
public static void ThrowPlainException(Exception e) =>
throw new Exception(Orleans.Runtime.LogFormatter.PrintException(e));
The solution I came to was to create a custom exception class that serializable add the database values object to it and bubble that up to the views.
[Serializable]
public class UpdateException : Exception
{
public object databaseValues { get; set; }
public UpdateException(object databaseValues)
{
this.databaseValues = databaseValues;
}
public UpdateException(string message, object databaseValues) :base(message)
{
this.databaseValues = databaseValues;
}
}

Entity object not updating

I created a static class that I want to use in various projects. I am trying to make this as independent as I can but it's not updating and I'm getting no error messages. I am using .Net Framework 4.0 client.
Here is the code:
using System;
using System.Linq;
namespace SSS.ServicesConfig.data
{
public partial class GlobalSetting
{
public static GlobalSetting GetGlobalSetting()
{
try
{
using (var context = new SuburbanEntities())
{
return (from gs in context.GlobalSettings
select gs).FirstOrDefault();
}
}
catch (Exception ex)
{
Logging.Log("An error occurred.", "GetGlobalSetting", Apps.ServicesConfig, ex);
throw new Exception(string.Format("Unable to retrieve data: [{0}].", ex.Message));
}
}
public static void SaveGlobalSettings(GlobalSetting globalSetting)
{
using (var context = new SuburbanEntities())
{
context.Attach(globalSetting);
context.SaveChanges();
}
}
}
}
Is the attach not done the right way? If not, how should I be saving it when it comes back in?
You have to set the state to modified:
context.Attach(globalSetting);
context.Entry(globalSetting).State = EntityState.Modified;
context.SaveChanges();
If i understand you correctly, I suggest you try removing the using statement in both methods and declare the context in this manner or better still a private varialbe to hold the local context variable.
var context = new SuburbanEntities())
context.Attach(globalSetting);
context.SaveChanges();
I understand you're in the ObjectContext API, so the way to mark an entity as changed is:
context.ObjectStateManager
.ChangeObjectState(globalSetting, EntityState.Modified);
after you attached the object to the context.
must mark it modified, try
context.Attach(globalSetting);
var gs = context.Entry(globalSetting);
gs.State = EntityState.Modified;
context.SaveChanges();

Application Settings saving

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.

Categories