I have a android app created with xamarin.
This is the code that crashes, not the first time, but often on the second time!
public void InsertOrUpdateInventoryWithoutEntries(Inventory inventory)
{
_logger.Debug("Enter");
try
{
var db = _dataBaseConnection.GetSqLiteConnection();
using (db)
{
var existingDbInventory = db.Find<DbInventory>(dbInventory => dbInventory.Code ==
inventory.Code);
if (existingDbInventory != null)
{
if (!existingDbInventory.Finished ) // do not update if finished.
{
existingDbInventory.Description = inventory.Description;
existingDbInventory.OpenTime = inventory.OpenTime;
existingDbInventory.Finished = inventory.Finished ;
existingDbInventory.UseInventoryList = inventory.UseInventoryList;
existingDbInventory.PostedToServer = inventory.PostedToServer;
existingDbInventory.InventoryListIsDownloaded =
inventory.InventoryListIsDownloaded;
UpdateInventory(existingDbInventory,db);
}
}
else
{
db.Insert(DbInventory.FromInventory(inventory));
}
db.Close();
}
}
catch
(SQLiteException ex)
{
_logger.Error(ex);
throw;
}
}
private void UpdateInventory(DbInventory inventory, SQLiteConnection db)
{
_logger.Debug("Enter");
try
{
var result = db.Update(inventory);
}
catch (SQLiteException ex)
{
_logger.Error(ex);
throw;
}
}
public bool InsertOrUpdateInventoryEntry(InventoryEntry inventoryEntryModel,
SQLiteConnection db=null)
{
_logger.Debug("Enter");
bool disposeFlg = false;
//detta då sqllite inte klarar av samtidiga anrop så bra, så man skall bara använda en
connection i taget !
if (db == null)
{
db = _dataBaseConnection.GetSqLiteConnection();
disposeFlg = true;
}
var existingDbInvetoryRow = db.Find<DbInventoryEntry>(dbInventoryRow =>
dbInventoryRow.ItemId == inventoryEntryModel.ItemId && dbInventoryRow.InventoryCode ==
inventoryEntryModel.InventoryCode);
if (existingDbInvetoryRow != null)
{
existingDbInvetoryRow.Cost = inventoryEntryModel.Cost;
existingDbInvetoryRow.Quantity = inventoryEntryModel.Quantity;
db.Update(existingDbInvetoryRow);
}
else
{
db.Insert(DbInventoryEntry.FromInventoryEntry(inventoryEntryModel));
}
if (disposeFlg)
{
db.Close();
db.Dispose();
}
return true;
}
private bool InsertInventoryRows(IEnumerable<InventoryEntry> inventoryEntryModels)
{
_logger.Debug("Enter");
var dbRows = inventoryEntryModels.Select(entry =>
(DbInventoryEntry.FromInventoryEntry(entry)));
var db = _dataBaseConnection.GetSqLiteConnection();
using (db)
{
db.InsertAll(dbRows);
db.Close();
}
return true;
}
The error I get is:
SQLite.SQLiteException: 'database is locked' or SQLite.SQLiteException: 'database is busy'
I found the solution thanks to Jason - github.com/praeclarum/sqlite-net/issues/700
I would advise you to keep a single SQLiteConnection for your app and cache it to take advantage of the type mapping caching strategy. Opening it with the Create | ReadWrite | FullMutex flags will ensure all operations are multithread-wise serialized. Don't forget to Dispose the connection whenever your app closes
This worked perfectly and speeded up the app ! Thanks Jason !
What i did was to handle one static connection that i held open all the time !
Related
I have this issue in production with MySQL connections where the connection is either being used or it's not opened error comes up frequently. I think when multiple requests hit the method then this issue is happening. I got stats from production were 80 different instances hit the Graphql method. I am using Sqlkata to make DB calls. When there are no parallel calls then it works fine.
ConnectionManager.cs
public IDbConnection GetConnection
{
get
{
if (_iDbConnection != null)
{
if (_iDbConnection.State != ConnectionState.Open)
{
_iDbConnection.Open();
return _iDbConnection;
}
}
if (_iDbConnection != null && _iDbConnection.State == ConnectionState.Open)
{
// close the previous connection
_iDbConnection.Close();
// open new connection
_iDbConnection.Open();
return _iDbConnection;
}
try
{
string connectionString = Configuration.GetConnectionString("seasonal");
// Register the factory
DbProviderFactories.RegisterFactory("MySqlConnector", MySqlConnectorFactory.Instance);
// Get the provider invariant names
IEnumerable<string> invariants = DbProviderFactories.GetProviderInvariantNames();
var factory = DbProviderFactories.GetFactory(invariants.FirstOrDefault());
var conn = factory.CreateConnection();
if (conn != null)
{
conn.ConnectionString = connectionString;
conn.Open();
_iDbConnection = conn;
}
}
catch (Exception e)
{
throw;
}
return _iDbConnection;
}
}
Service (constructor)
public OutboundRecordService(IConnectionManager connectionManager) : base(connectionManager)
{
IDbConnection connection = connectionManager.GetConnection;
if (connection.State != ConnectionState.Open)
{
_logger.Error($"Connection is not open, current state: {connection.State}");
}
_queryFactory = new QueryFactory(connection, new MySqlCompiler());
_logger = LogManager.GetLogger(typeof(OutboundRecordService<T>));
}
public async Task<CareMetx.Service.Models.PaginationResult<OutboundRecord>> GetAllByCriteria(OutboundRecordSearchCriteria searchCriteria)
{
try
{
// START - Test code
var arrayTask = new List<Task>();
int i = 10;
for (int c = 0; c < i; c++)
{
arrayTask.Add(Task.Factory.StartNew(() => GetDataFromDB(searchCriteria)));
}
Task.WaitAll(arrayTask.ToArray());
Console.WriteLine("All threads complete");
return await Task.FromResult<CareMetx.Service.Models.PaginationResult<OutboundRecord>>(null);
// END - Test code
}
catch (Exception ex)
{
var message = ex.Message;
}
return null;
}
private async Task<List<OutboundRecord>> GetDataFromDB(OutboundRecordSearchCriteria searchCriteria)
{
Query dbQuery = _queryFactory.Query("SELECT * FROM OutboundRecords Where BatchId = 1");
// Clone the query and get the total count
var totalCountQuery = dbQuery.Clone();
totalCountQuery = totalCountQuery.AsCount();
// Log Record Count SQL
LogSqlQuery(totalCountQuery);
// Get the total record count
var totalRecords = await totalCountQuery.FirstOrDefaultAsync<int>();
}
Exception
var randnumber = CommonClass.Generate8DigitHBFNumber();
bool CheckCaseRef = CheckCaseRefIdAlreadyExistsInDB(randnumber);
if (CheckCaseRef)
{
randnumber = CommonClass.Generate8DigitHBFNumber();
}
else
{
randnumber = CommonClass.Generate8DigitHBFNumber();
}
//Method to Check the generated random number
bool CheckCaseRefIdAlreadyExistsInDB(string randnumber)
{
Log.Info("CheckCaseRefIdAlreadyExistsInDB started...");
bool checkCaseRef = false;
try
{
var ObjCustomerList = db.tblCustomers.ToList();
if (ObjCustomerList != null)
{
foreach (var customerlst in ObjCustomerList)
{
if (!(string.IsNullOrEmpty(randnumber)))
{
if (customerlst.CaseRef == randnumber)
{
checkCaseRef = true;
break;
}
}
}
}
else
{
return checkCaseRef;
}
}
catch (Exception ex)
{
Log.Error("Error CheckCaseRefIdAlreadyExistsInDB started...", ex);
return false;
}
return checkCaseRef;
}**
You might want to do this:
var randnumber = CommonClass.Generate8DigitHBFNumber();
while (! CheckCaseRefIdAlreadyExistsInDB(randnumber))
{
randnumber = CommonClass.Generate8DigitHBFNumber();
}
bool CheckCaseRefIdAlreadyExistsInDB(string randnumber)
{
return db.tblCustomers.Any(c => c.CaseRef == randnumber ?? "");
}
Checking the regenerated one would be an excellent use for recursion. If you don't know much about recursion, I would highly recommend doing some research on it first though, as it can lead to some really nasty memory issues in your code if used incorrectly.
//Code in main method
string randnumber = CheckRandomNumber();
//Recursive method to call
public string CheckRandomNumber()
{
string numToCheck = CommonClass.Generate8DigitHBFNumber();
if (db.tblCustomers.Any(x => x.CaseRef == numToCheck))
{
//Duplicate was found
CheckRandomNumber();
}
return numToCheck;
}
I'm fairly new to EF transactions, this is the code that is used for saving
public bool Save(TbArea area, bool isNew, out string errMsg)
{
try
{
errMsg = string.Empty;
using (var oScope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(120)))
{
try
{
TbArea oEntity = oContext.TbArea.Where(a => a.AreaCode == area.AreaCode && a.CompanyId == MainClass.SystemCompanyId).FirstOrDefault();
if (oEntity != null)
{
if (isNew) { errMsg = Resources.ResSales.MsgRecordCodeDublicated; return false; }
oContext.TbArea.Attach(oEntity);
oContext.Entry(oEntity).CurrentValues.SetValues(area);
}
else
{
if (!isNew) { errMsg = Resources.ResSales.MsgRecordNotFoundInDB; return false; }
oContext.TbArea.Add(area);
}
oContext.SaveChangesAsync();
oScope.Complete();
return true;
}
catch (Exception ex)
{
oScope.Dispose();
errMsg = ex.Message;
return false;
}
}
I'm overriding SaveChangesAsync
so that I can save the ChangeTracker.Entries into the database.
this is a portion of the code:
dbContext.AcceptAllChanges();
logsSet.AddRange(audits);
int result = 0;
try
{
result = await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
//scope.Complete(); there was a transaction that I commented out, I thought it might overlaps the original transaction!
return result;
}
catch (Exception ex)
{
var m = ex.Message;
return result;
}
When I save an item, I get the error :
the transaction has aborted
when I remove the transaction scope the saving happens normally!
Your code is marking the transaction complete before the changes have finished saving:
oContext.SaveChangesAsync();
oScope.Complete();
You need to use await:
await oContext.SaveChangesAsync();
oScope.Complete();
If you're in a context where await can resume on a different thread, you'll probably also need to specify TransactionScopeAsyncFlowOption.Enabled.
I have this method :
public void Update(DBS.BankAccount entity)
{
try
{
using (var _nahidContext = new NahidContext())
{
_nahidContext.BankAccounts.Attach(entity);
var bankAccountElement = _nahidContext.Entry(entity);
bankAccountElement.CurrentValues.SetValues(entity);
_nahidContext.SaveChanges();
//__________ or ___________
//var bankAccountElement = FindById(entity.Id);
//if (_nahidContext.Entry(bankAccountElement).State == System.Data.Entity.EntityState.Detached)
//{
// _nahidContext.BankAccounts.Attach(bankAccountElement);
//}
////_nahidContext.Entry(bankAccountElement).State = System.Data.Entity.EntityState.Modified;
//_nahidContext.SaveChanges();
}
}
catch (Exception ex)
{
throw new ArgumentException(ex.Message);
}
}
Which run without any error but my bankAccountElement does not change.
Please help me.
First you attach (entity state= unchanged), then you set values with same values than attached entity, so it keeps unchanged.
You should do this
_nahidContext.BankAccounts.Attach(entity);
var bankAccountElement = _nahidContext.Entry(entity);
bankAccountElement.State = EntityState.Modified;
You can read more here
The code below is not working. (no exceptions thrown). Totally clueless why is not changed When I check in the GUI, there is a new version, with no changes!
public static void SetEntityWebName(ProcessEntity entity, SPWeb entityWeb)
{
try
{
entityWeb.AllowUnsafeUpdates = true;
var welcomePageListItem = entityWeb.GetFile(entityWeb.RootFolder.WelcomePage).Item;
var welcomePage = entityWeb.GetFile(entityWeb.RootFolder.WelcomePage);
welcomePage.CheckOut();
if (entity.Type == Entity.Job)
{
entityWeb.Title = ((SyncJobs_Result)entity.Entity).JobName;
welcomePageListItem["Title"] = ((SyncJobs_Result)entity.Entity).JobName;
welcomePage.Update();
}
if (entity.Type == Entity.Client)
{
entityWeb.Title = ((SyncClients_Result)entity.Entity).ClientName;
welcomePageListItem["Title"] = ((SyncClients_Result)entity.Entity).ClientName;
welcomePage.Update();
}
if (entity.Type == Entity.Opportunity)
{
entityWeb.Title = ((SyncOpportunities_Result)entity.Entity).OpportunityName;
welcomePageListItem["Title"] = ((SyncOpportunities_Result)entity.Entity).OpportunityName;
welcomePage.Update();
}
welcomePage.CheckIn(string.Empty);
welcomePage.Publish(string.Empty);
entityWeb.Update();
}
catch (Exception ex)
{
}
}
I think you also have to update the welcomePageListItem list item .
I am not sure but , give it a try