// constructor
public ADALTokenCache(string user)
{
// associate the cache to the current user of the web app
User = user;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the DB
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
// place the entry in memory
this.Deserialize((Cache == null) ? null : Cache.cacheBits);
}
// clean up the DB
public override void Clear()
{
base.Clear();
foreach (var cacheEntry in db.UserTokenCacheList)
db.UserTokenCacheList.Remove(cacheEntry);
db.SaveChanges();
}
// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
if (Cache == null)
{
// first time access
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
}
else
{ // retrieve last write from the DB
var status = from e in db.UserTokenCacheList
where (e.webUserUniqueId == User)
select new
{
LastWrite = e.LastWrite
};
// if the in-memory copy is older than the persistent copy
if (status.First().LastWrite > Cache.LastWrite)
//// read from from storage, update in-memory copy
{
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
}
}
this.Deserialize((Cache == null) ? null : Cache.cacheBits);
}
// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (this.HasStateChanged)
{
Cache = new UserTokenCache
{
webUserUniqueId = User,
cacheBits = this.Serialize(),
LastWrite = DateTime.Now
};
//// update the DB and the lastwrite
db.Entry(Cache).State = Cache.UserTokenCacheId == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
this.HasStateChanged = true;
}
}
void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}
This is my code for manage AdalToken Cache
I got the following exception
Failed to acquire token silently. Call method AcquireToken
got this exception in following code near to var authResultDisc
//SharePoint connection to get List Items
DiscoveryClient discClient = new DiscoveryClient(SettingsHelper.DiscoveryServiceEndpointUri,
async () =>
{
var authResultDisc = await authContext.AcquireTokenSilentAsync(SettingsHelper.DiscoveryServiceResourceId, new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResultDisc.AccessToken;
});
var dcr = await discClient.DiscoverCapabilityAsync("RootSite");
my thinking on this is that, it dose not clearing the database entries.
Can any one help me to solve this error.
This is an expected exception if the access token is not able to find in the cache and refresh the access token failed. Please check whether whether you acquire the access token before you call this function.
Related
In my method below which is written using c# in asp.net core, I am getting error when executing any of the below update statements. What is the issue with using await SaveContextAsync(); I have noticed I dont get error when only using SaveContext();
The error that I am getting is as follows
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection
and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose()
on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency
injection container take care of disposing context instances.
Object name: 'FXDB1Context'.
I am not sure why I am getting this error.
public async Task<IdentityResult> ApproveUserChangeRequest(UserChangeRequest userChangeRequest, string approvedByAuthUserName, string authApplicationName)
{
var userChangeRequestRepository = UserChangeRequestRepository.GetAllAsList();
var userChangeRequestApprovalRepository = UserChangeRequestApprovalRepository.GetAllAsList();
var appSettingRepository = AppSettingRepository.GetAllAsList();
var clientCompanyContactRepository = ClientCompanyContactRepository.GetAllAsList();
var applicationUserRepo = ApplicationUserRepo.GetAllAsList();
int approvedByAuthUserID = GetApprovedByUserId(authApplicationName, approvedByAuthUserName);
// Check if UserChangeRequest is still Pending
bool isUserChangeRequestPending = userChangeRequestRepository.Any(x => x.Id == userChangeRequest.Id && x.ChangeStatus == "Pending");
if (isUserChangeRequestPending && approvedByAuthUserID > 0)
{
// Inserting record in the UserChangeRequestApproval table
InsertUserChangeRequestApproval(userChangeRequest);
await SaveContextAsync();
using (var userTransaction = Context.Database.BeginTransaction())
{
using (var securityTransaction = _securityContext.Database.BeginTransaction())
{
try
{
//Get the Number of approval required for Internal and External Users
int? internalApprovalsRequired = GetApprovals("InternalUserChangeRequestApprovalsRequired", appSettingRepository);
int? externalApprovalsRequired = GetApprovals("ExternalUserChangeRequestApprovalsRequired", appSettingRepository);
//Get the name of the application the auth user belongs to
var authUserApplicationName = GetApplicationName(userChangeRequest.AuthUserId);
//Get the Number of approvals for the request
var numberOfAprovals = userChangeRequestApprovalRepository.Where(x => x.UserChangeRequestId == userChangeRequest.Id).Count();
//If the number of approvals is equal or greater than the Approvals required then Update AppUser or Contact details
if ((authUserApplicationName == "ArgentexTrader" && numberOfAprovals >= internalApprovalsRequired) || (authUserApplicationName == "ArgentexClient" && numberOfAprovals >= externalApprovalsRequired))
{
//Updating the clientcontact table
UpdateClientContact(userChangeRequest, clientCompanyContactRepository);
//Updating the auth user table
UpdateAuthUser(userChangeRequest);
//Updating the IdentityDB user table
UpdateIdentityDBUser(userChangeRequest, applicationUserRepo);
//Updating the UserChangeRequest table
userChangeRequest.ChangeStatus = "Approved";
UserChangeRequestRepository.Update(userChangeRequest);
await SaveContextAsync();
userTransaction.Commit();
securityTransaction.Commit();
return IdentityResult.Success;
}
}
catch (Exception ex)
{
userTransaction.Rollback();
securityTransaction.Rollback();
_logger.Error(ex);
return IdentityResult.Failed(new IdentityError { Description = ex.Message });
}
}
}
}
return null;
}
The error says that a resolved context is been trying to made Disposable.
For More Information: Using Statements in C#
using statement is a scope only process, so this error means that your program tries to use the same instances of "context" in somewhere else, which it cant use it, because as I mentioned it is a "scope only" process.
In your example:
using (var userTransaction = Context.Database.BeginTransaction())
{
using (var securityTransaction = _securityContext.Database.BeginTransaction())
{
}
}
You can make it simple and dont use a using scope or you can be sure that, the context is just used in those using scopes.
I basically took code from here https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-multitenant-openidconnect/blob/master/TodoListWebApp/DAL/EFADALTokenCache.cs but it is not suitable for my application as I don't need the cache per user as given in the example. Accordingly I removed the constructor that accepted User as a parameter since I wanted the cache to be global. I have came up with this version:
public class EFTestTokenCache : TokenCache
{
private TestEntities _TestEntities = new TestEntities();
private TestTokenCache _cache;
public EFTestTokenCache()
{
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
}
// clean up the DB
public override void Clear()
{
base.Clear();
foreach (var cacheEntry in _TestEntities.TestTokenCaches)
_TestEntities.TestTokenCaches.Remove(cacheEntry);
_TestEntities.SaveChanges();
}
// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
if (_cache == null)
{
// first time access
_cache = _TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);
}
else
{ // retrieve last write from the DB
var status = from e in _TestEntities.TestTokenCaches
where (e.webUserUniqueId == args.DisplayableId)
select new
{
LastWrite = e.LastWrite
};
// if the in-memory copy is older than the persistent copy
if (status.First().LastWrite > _cache.LastWrite)
//// read from from storage, update in-memory copy
{
_cache = _TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);
}
}
this.Deserialize((_cache == null) ? null : _cache.cacheBits);
}
// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (this.HasStateChanged)
{
if (_cache != null)
{
_cache.cacheBits = this.Serialize();
_cache.LastWrite = DateTime.Now;
}
else
{
_cache = new TestTokenCache
{
webUserUniqueId = args.DisplayableId,
cacheBits = this.Serialize(),
LastWrite = DateTime.Now
};
}
// update the DB and the lastwrite
_TestEntities.Entry(_cache).State = _cache.EntryId == 0 ? EntityState.Added : EntityState.Modified;
_TestEntities.SaveChanges();
this.HasStateChanged = false;
}
}
void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}
}
Do you think this would work fine as a global cache or is it buggy and always has to be user based as given in the example?
Another query is why is the database cleared in Clear(). Does this mean whenever application pool shuts down or so my database would be cleared? That should not happen though.
Any help is appreciated.
If you are trying to implement a global token cache irrespective of the user then I see an issue with your code as code is looking for any existing cache per the sign in user
as code is using webUserUniqueId to filter
_TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);
In the correct sample code, every user has a set of tokens that are saved in a DB (or as collection), so that when they sign in to the web app they can directly perform their web API calls without having to re-authenticate/repeat consent.
I am not sure of the purpose why you want to do this but in my opinion if you are implementing a custom token cache for a web it would be good to provide the desirable level of isolation between tokens for different users signing in.
Also, Clear() method clears the cache by deleting all the items in db but this method has not been called in the GitHub sample and you need to add a call to authContext.TokenCache.clear() from SignOut() method of AccountController to clear the cache on user signout.
i tried this method that I created but it prompts me an error:
Realms.RealmInvalidObjectException:This object is detached. Was it deleted from the realm?'
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
using (var transaction = Realm.GetInstance(config).BeginWrite())
{
Realm.GetInstance(config).Remove(denom_list[0]);
transaction.Commit();
}
}
what is the proper coding for deleting records from database in realm in C# type of coding?
You are doing it the right way. The error message you are getting indicates that the object was removed already. Are you sure it still exists in the realm?
UPDATE:
I decided to update this answer because my comment on the other answer was a bit hard to read.
Your original code should work fine. However, if you want deleteFromDatabase to accept lists with CashDenomination instances that either have been removed already or perhaps were never added to the realm, you would need to add a check. Furthermore, note that you should hold on to your Realm instance and use it in the transaction you created. In most cases, you want to keep it around even longer, though there is little overhead to obtaining it via GetInstance.
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
if (!denom_list[0].IsValid) // If this object is not in the realm, do nothing.
return;
var realm = Realm.GetInstance(config);
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom_list[0]);
transaction.Commit();
}
}
Now, if you want to use identifiers, you could look it up like you do, but still just use Remove:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.All<CashDenomination>().FirstOrDefault(c => c.denom_id == denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
Finally, if your CashDenomination has denom_id marked as PrimaryKey, you could look it up like this:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.ObjectForPrimaryKey<CashDenomination>(denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
public void deleteFromDatabase(Realm realm, long cashDenominatorId)
{
realm.Write(() =>
{
var cashDenominator = realm.All<Person>().Where(c => c.Id == cashDenominatorId);
Realm.RemoveRange<CashDenomination>(((RealmResults<CashDenomination>)cashDenominator));
});
}
Which you would call as
Realm realm = Realm.GetInstance(config);
var denom_list = ...
// ...
deleteFromDatabase(realm, denom_list[0].id);
I already made it having this code :) thanks to #EpicPandaForce 's answer.
public void deleteFromDatabase(int denom_ID, int form_ID)
{
//Realm realm;
//and
//RealmConfiguration config = new RealmConfiguration(dbPath, true);
//was initialized at the top of my class
realm = Realm.GetInstance(config);
realm.Write(() =>
{
var cashflow_denom = realm.All<CashDenomination>().Where(c => c.denom_id == denom_ID);
var cashflow_form = realm.All<CashForm>().Where(c => c.form_id == form_ID);
realm.RemoveRange(((RealmResults<CashDenomination>)cashflow_denom));
realm.RemoveRange(((RealmResults<CashForm>)cashflow_form));
});
}
it is now deleting my data without exception :)
I have added an attribute DisableConcurrentExecution(1) on the job, but all that does is delays the execution of second instance of a job until after first one is done. I want to be able to detect when a concurrent job has been run, and then cancel it all together.
I figured, if DisableConcurrentExecution(1) will prevent two instances of same recurrent job from running at the same, it will put the second job on "retry", thus changing it's State. So I added additional custom attribute on the job, which detects failed state, like so :
public class StopConcurrentTask : JobFilterAttribute, IElectStateFilter
{
public void OnStateElection(ElectStateContext context)
{
var failedState = context.CandidateState as FailedState;
if(failedState != null && failedState.Exception != null)
{
if(!string.IsNullOrEmpty(failedState.Exception.Message) && failedState.Exception.Message.Contains("Timeout expired. The timeout elapsed prior to obtaining a distributed lock on"))
{
}
}
}
}
This allows me to detect whether a job failed due to being run concurrently with another instance of same job. The problem is, I can't find a way to Cancel this specific failed job and remove it from being re-run. As it is now, the job will be put on retry schedule and Hangfire will attempt to run it a number of times.
I could of course put an attribute on the Job, ensuring it does not Retry at all. However, this is not a valid solution, because I want jobs to be Retried, except if they fail due to running concurrently.
You can prevent retry to happen if you put validation in OnPerformed method in IServerFilter interface.
Implementation :
public class StopConcurrentTask : JobFilterAttribute, IElectStateFilter, IServerFilter
{
// All failed after retry will be catched here and I don't know if you still need this
// but it is up to you
public void OnStateElection(ElectStateContext context)
{
var failedState = context.CandidateState as FailedState;
if (failedState != null && failedState.Exception != null)
{
if (!string.IsNullOrEmpty(failedState.Exception.Message) && failedState.Exception.Message.Contains("Timeout expired. The timeout elapsed prior to obtaining a distributed lock on"))
{
}
}
}
public void OnPerformed(PerformedContext filterContext)
{
// Do your exception handling or validation here
if (filterContext.Exception == null) return;
using (var connection = _jobStorage.GetConnection())
{
var storageConnection = connection as JobStorageConnection;
if (storageConnection == null)
return;
var jobId = filterContext.BackgroundJob.Id
// var job = storageConnection.GetJobData(jobId); -- If you want job detail
var failedState = new FailedState(filterContext.Exception)
{
Reason = "Your Exception Message or filterContext.Exception.Message"
};
using (var transaction = connection.GetConnection().CreateWriteTransaction())
{
transaction.RemoveFromSet("retries", jobId); // Remove from retry state
transaction.RemoveFromSet("schedule", jobId); // Remove from schedule state
transaction.SetJobState(jobId, failedState); // update status with failed state
transaction.Commit();
}
}
}
public void OnPerforming(PerformingContext filterContext)
{
// Do nothing
}
}
I hope this will help you.
I actually ended up using based on Jr Tabuloc answer - it will delete a job if it has been last executed 15 seconds ago - I noticed that time between server wake up and job execution varies. Usually it is in milliseconds, but since my jobs are executed once a day, I figured 15sec won't hurt.
public class StopWakeUpExecution : JobFilterAttribute, IServerFilter
{
public void OnPerformed(PerformedContext filterContext)
{
}
public void OnPerforming(PerformingContext filterContext)
{
using (var connection = JobStorage.Current.GetConnection())
{
var recurring = connection.GetRecurringJobs().FirstOrDefault(p => p.Job.ToString() == filterContext.BackgroundJob.Job.ToString());
TimeSpan difference = DateTime.UtcNow.Subtract(recurring.LastExecution.Value);
if (recurring != null && difference.Seconds < 15)
{
// Execution was due in the past. We don't want to automaticly execute jobs after server crash though.
var storageConnection = connection as JobStorageConnection;
if (storageConnection == null)
return;
var jobId = filterContext.BackgroundJob.Id;
var deletedState = new DeletedState()
{
Reason = "Task was due in the past. Please Execute manually if required."
};
using (var transaction = connection.CreateWriteTransaction())
{
transaction.RemoveFromSet("retries", jobId); // Remove from retry state
transaction.RemoveFromSet("schedule", jobId); // Remove from schedule state
transaction.SetJobState(jobId, deletedState); // update status with failed state
transaction.Commit();
}
}
}
}
}
I have simple query that loads data from two tables into GUI. I'm saving loaded data to widely available object Clients currentlySelectedClient.
using (var context = new EntityBazaCRM())
{
currentlySelectedClient = context.Kliencis.Include("Podmioty").FirstOrDefault(d => d.KlienciID == klientId);
if (currentlySelectedClient != null)
{
textImie.Text = currentlySelectedClient.Podmioty.PodmiotOsobaImie;
textNazwisko.Text = currentlySelectedClient.Podmioty.PodmiotOsobaNazwisko;
}
else
{
textNazwa.Text = currentlySelectedClient.Podmioty.PodmiotFirmaNazwa;
}
}
So now if I would like to:
1) Save changes made by user how do I do it? Will I have to prepare something on database side? How do I handle modifying multiple tables (some data goes here, some there)? My current code seems to write .KlienciHaslo just fine, but it doesn't affect Podmioty at all. I tried different combinations but no luck.
2) Add new client to database (and save information to related tables as well)?
currentClient.Podmioty.PodmiotOsobaImie = textImie.Text; // not saved
currentClient.Podmioty.PodmiotOsobaNazwisko = textNazwisko.Text; // not saved
currentClient.KlienciHaslo = "TEST111"; // saved
using (var context = new EntityBazaCRM())
{
var objectInDB = context.Kliencis.SingleOrDefault(t => t.KlienciID == currentClient.KlienciID);
if (objectInDB != null)
{
// context.ObjectStateManager.ChangeObjectState(currentClient.Podmioty, EntityState.Modified);
//context.Podmioties.Attach(currentClient.Podmioty);
context.Kliencis.ApplyCurrentValues(currentClient); // update current client
//context.ApplyCurrentValues("Podmioty", currentClient.Podmioty); // update current client
}
else
{
context.Kliencis.AddObject(currentClient); // save new Client
}
context.SaveChanges();
}
How can I achieve both?
Edit for an answer (doesn't save anything ):
currentClient.Podmioty.PodmiotOsobaImie = textImie.Text; // no save
currentClient.Podmioty.PodmiotOsobaNazwisko = textNazwisko.Text; // no save
currentClient.KlienciHaslo = "TEST1134"; // no save
using (var context = new EntityBazaCRM())
{
if (context.Kliencis.Any(t => t.KlienciID == currentClient.KlienciID))
{
context.Kliencis.Attach(currentClient); // update current client
}
else
{
context.Kliencis.AddObject(currentClient); // save new Client
}
context.SaveChanges();
}
Apparently ApplyCurrentValues only works with scalar properties.
If you attach the currentClient then associated objects should also attach, which means they'll be updated when you SaveChanges()
But you'll get an Object with the key exists exception because you are already loading the object from the database into the objectInDB variable. The context can only contain one copy of a Entity, and it knows that currentClient is the same as objectInDB so throws an exception.
Try this pattern instead
if (context.Kliencis.Any(t => t.KlienciID == currentClient.KlienciID))
{
context.Kliencis.Attach(currentClient); // update current client
}
else
{
context.Kliencis.AddObject(currentClient); // save new Client
}
or if you're using an identity as the ID, then
// if the ID is != 0 then it's an existing database record
if (currentClient.KlienciID != 0)
{
context.Kliencis.Attach(currentClient); // update current client
}
else // the ID is 0; it's a new record
{
context.Kliencis.AddObject(currentClient); // save new Client
}
After some work and help from Kirk about the ObjectStateManager error that I was getting I managed to fix this. This code allows me to save both changes to both tables.
currentClient.Podmioty.PodmiotOsobaImie = textImie.Text;
currentClient.Podmioty.PodmiotOsobaNazwisko = textNazwisko.Text;
currentClient.KlienciHaslo = "TEST1134";
using (var context = new EntityBazaCRM()) {
if (context.Kliencis.Any(t => t.KlienciID == currentClient.KlienciID)) {
context.Podmioties.Attach(currentClient.Podmioty);
context.Kliencis.Attach(currentClient);
context.ObjectStateManager.ChangeObjectState(currentClient.Podmioty, EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(currentClient, EntityState.Modified);
} else {
context.Kliencis.AddObject(currentClient); // save new Client
}
context.SaveChanges();
}