sqflite database getting locked flutter - Warning database has been locked - flutter

I am using SQLite in my flutter project and trying to figure out DB locked issue, in my scenario user is trying to download new data one time in day, and if that record exist it will update or it will insert a new record. My problem is even though I am using transaction and batch I am getting DB locked error, the only issue I can think of is getSongList() call as it calls DB out of that transaction or batch multiple times but that's read call and my code seems to fail during batch commit.
buildDB1(List<MusicData> _list, int version) async {
await openDb();
try {
_database.transaction((txn) async {
Batch batch = txn.batch();
for (var i = 0; i < _list.length; i++) {
// buildBatch(_list[i]);
MusicData musicData = _list[i];
int id = musicData.id;
if (musicData.pdfpage == 0 || musicData.pdfpage == null) {
PDFPAGE = "0";
} else {
PDFPAGE = (musicData.pdfpage).toString();
}
if (musicData.linkid == 0 || musicData.linkid == null) {
LINKID = "0";
} else {
LINKID = (musicData.linkid).toString();
}
// PDFPAGE = musicData.pdfpage as String;
// LINKID = musicData.linkid as String;
TITLE = musicData.title;
ALBUM = musicData.album;
SONGURL = musicData.songURL;
HINDINAME = musicData.hindiName;
MNAME = musicData.mname;
MSIGN = musicData.msign;
OTHER1 = musicData.other1;
OTHER2 = musicData.other2;
ENAME = musicData.ename;
ESIGN = musicData.esign;
LANGUAGE = musicData.language;
SONGTEXT = musicData.songtext;
Future<List<MusicData>> list1 =
getSongList("select * from songs where id=$id");
List<MusicData> list = await list1;
if (list.length != 0) {
String updateSQL =
"UPDATE SONGS SET pdfpage = $PDFPAGE, linkid = $LINKID, title = '$TITLE', album = '$ALBUM', songURL = '$SONGURL', hindiName = '$HINDINAME', mname = '$MNAME', msign = '$MSIGN', other1 = '$OTHER1', other2 = '$OTHER2', ename = '$ENAME', esign = '$ESIGN', language = '$LANGUAGE',songtext = '$SONGTEXT' WHERE id = $ID";
batch.rawUpdate(updateSQL);
// _database.rawUpdate(
// "UPDATE SONGS SET pdfpage = ?, linkid = ?, title = ?, album = ?, songURL = ?, hindiName = ?, mname = ?, msign = ?, other1 = ?, other2 = ?, ename = ?, esign = ?, language = ?,songtext = ? WHERE id = ?",
// [
// musicData.id,
// musicData.pdfpage,
// musicData.linkid,
// musicData.title,
// musicData.album,
// musicData.songURL,
// musicData.hindiName,
// musicData.mname,
// musicData.msign,
// musicData.other1,
// musicData.other2,
// musicData.ename,
// musicData.esign,
// musicData.language,
// musicData.songtext
// ]);
print("Record updated in db $id");
// _database.close();
} else {
String insertSQL =
"INSERT INTO SONGS (pdfpage, linkid, title,album,songURL,hindiName,mname,msign,other1,other2,ename,esign,language,songtext,isfav) VALUES ($PDFPAGE,$LINKID,'$TITLE','$ALBUM','$SONGURL','$HINDINAME','$MNAME','$MSIGN', '$OTHER1','$OTHER2','$ENAME','$ESIGN','$LANGUAGE','$SONGTEXT',0)";
batch.rawInsert(insertSQL);
// _database.insert('SONGS', musicData.toMap());
print("Record inserted in db $id");
}
}
Future<List> result = batch.commit();
});
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('dbversion', version);
} catch (e) {
print(e);
}
}

getSongList should take a transaction argument. Basically use txn instead of _database in any db calls during a transaction. Otherwise it will hang and the warning is correct.
Also you might be hitting some race condition since you are not awaiting for batch.commit before the end of the transaction. You can try to replace:
Future<List> result = batch.commit();
by
await batch.commit();
Using pedantic could warn you about a missing await here.

Related

Filter data from Stream in Flutter/Dart

I have a Stream which contains List of objects. i.e. List<ProductSubCategoryListModel> and
ProductSubCategoryListModel contains isSelected boolean and CategoryId int.
I want to filter Stream which only has isSelected == true and get list type of List<ProductSubCategoryListModel> in return. After this need to get all the CategoryId from `List.
Here's my code.
Future<void> fetchProducts(
TrendingCategoriesModel categoryModel, int pageIndex) async {
_localCategoryModel = categoryModel;
//Stream I want to manipulate to get List<ProductSubCategoryListModel>
var stream = _categoryListController.stream;
var request = ProductListRequestModel();
var searchParameterModel = SearchParams();
searchParameterModel.SearchText = '';
searchParameterModel.CategoryIDs = //List<int> of CategoryIds filtered from List<ProductSubCategoryListModel>;
searchParameterModel.SubCategoryIDs = null;
searchParameterModel.AttributeIDs = null;
searchParameterModel.SubAttributeIDs = null;
searchParameterModel.StartPrice = 0;
searchParameterModel.EndPrice = null;
searchParameterModel.EndPrice = null;
request.PageIndex = pageIndex;
request.PageSize = 10;
request.SortExpression = '';
request.SortType = '';
request.IsGetFilterData = true;
request.searchParams = searchParameterModel;
if (!_productListController.isClosed) _productListController.sink.add(null);
var response =
await _networkHandler.callAPI(NetworkUrl.PRODUCT_LIST, request);
if (response != null) {
}
}
You can use map method https://api.flutter.dev/flutter/dart-async/Stream/map.html. it returns new stream with mapped data. Map will be applied to each event in a stream.
Additionally you can apply distinct method https://api.flutter.dev/flutter/dart-async/Stream/distinct.html just after map, if you need to ensure that only unique values will be emitted to the subscriber

Trying to Move Cards

I am trying to move a card I have found using the search function from one list to another on the same board.
I have tried to set up a new list using the a new factory.list and searching for the list using FirstorDefault LINQ Query but keep getting an Error.
Below is some code I have tried to move my cards.
string query = jnum;
var search = factory.Search(query, 1, SearchModelType.Cards, new IQueryable[] { board });
await search.Refresh();
var CardList = search.Cards.ToList();
foreach (var card in CardList)
{
string tName = card.Name.Substring(0, 6);
if (tName == jnum.Trim())
{
cardid = card.Id;
}
}
var FoundCard = factory.Card(cardid);
string FoundListid = FoundCard.List.Id;
var fromlist = factory.List(FoundListid);
if (Item != null)
{
if (mail.Body.ToUpper().Contains("Approved for Print".ToUpper()))
{
//var ToList = factory.List("5db19603e4428377d77963b4");
var ToList = board.Lists.FirstOrDefault(l => l.Name == "Signed Off");
FoundCard.List = ToList;
// from on proof
//MessageBox.Show("Approved for Print");
}
else if (mail.Body.ToUpper().Contains("Awaiting Review".ToUpper()))
{
//var ToList = factory.List("5db19603e4428377d77963b3");
var ToList = board.Lists.FirstOrDefault(l => l.Name == "On Proof");
FoundCard.List = ToList;
// from in progress or to start
// MessageBox.Show("Awaiting Review");
}
else if (mail.Body.ToUpper().Contains("Amends".ToUpper()))
{
var ToList = factory.List("5dc9442eb245e60a39b3d4a7");
FoundCard.List = ToList;
// from on proof
//MessageBox.Show("Amends");
}
else
{
// non job mail
}
}
I keep getting a is not a valid value Error.
Thanks for help

Entity Framework not saving to the database

I have a challenge that I am trying to update a record from database But it is not saving to the database, and not showing any errors, it is giving a message that it has updated the record but there are no changes to the database. My code is as below. Any suggestions
dbEntities context = new dbEntities();
var query = context.ConsultantsProfiles.SingleOrDefault(c => c.Username == username);
if (query != null)
{
query.Summary = txtSummary.Text;
query.CareerTitle = txtTitle.Text;
query.ConsultantType = cbType.Text;
query.Username = username;
query.FirstName = txtFirstname.Text;
query.LastName = txtLastName.Text;
query.Email = txtEmail.Text;
query.DateofBirth = Convert.ToDateTime(dptDateofBirth.Value);
query.PhoneNumber = txtPhoneNumber.Text;
query.Website = txtWebsite.Text;
query.Town = txtTown.Text;
query.Country = txtCountry.Text;
if (FileUpload1.HasFile)
{
//image upload
HttpPostedFile postedFile = FileUpload1.PostedFile;
// HttpPostedFile postedFile = uploadControl.UploadedFiles[i];
Stream stream = postedFile.InputStream;
BinaryReader reader = new BinaryReader(stream);
byte[] imgByte = reader.ReadBytes((int)stream.Length);
int imglength = FileUpload1.PostedFile.ContentLength;
query.ProfilePhoto = imgByte;
}
context.ConsultantsProfiles.Attach(query);
context.Entry(query).State = EntityState.Modified;
context.SaveChanges();
}
Response.Write("<script language=javascript>alert('Notification: The Profile Has been Updated');</script>");
}
dbEntities context = new dbEntities();
var consultantProfile = new ConsultantProfile
{
Summary = txtSummary.Text;
CareerTitle = txtTitle.Text;
ConsultantType = cbType.Text;
Username = username;
FirstName = txtFirstname.Text;
LastName = txtLastName.Text;
Email = txtEmail.Text;
DateofBirth = Convert.ToDateTime(dptDateofBirth.Value);
PhoneNumber = txtPhoneNumber.Text;
Website = txtWebsite.Text;
Town = txtTown.Text;
Country = txtCountry.Text;
}
if (FileUpload1.HasFile)
{
//image upload
HttpPostedFile postedFile = FileUpload1.PostedFile;
// HttpPostedFile postedFile = uploadControl.UploadedFiles[i];
Stream stream = postedFile.InputStream;
BinaryReader reader = new BinaryReader(stream);
byte[] imgByte = reader.ReadBytes((int)stream.Length);
int imglength = FileUpload1.PostedFile.ContentLength;
consultantProfile.ProfilePhoto = imgByte;
}
context.Entry(ConsultantsProfiles).State = EntityState.Modified;
context.SaveChanges();

ConfirmEmailAsync() method is not working

I am having issue in confirming new user email. the Confirm email link works for first 20 minutes , but after 50 minutes the link expires. I have set the token expiration time to 24 hours. Please help me in resolving this issue. I am stuck on it for last 2 days:(.My code is as follows:
I am setting the token lifetime in Create() method in ApplicationUserManager as following:
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = _settings.ConfirmationAndResetTokenExpirationTimeSpan
};
}
And then In AccountsController, the Create method for new user is geiven below. The SendEmailAsync method consist of email subject, email body, generated password and the callback uri.
[Authorize(Roles = Roles.Bam.Name.Admin)]
[HttpPost]
[Route(Routes.Accounts.Template.Create, Name = Routes.Accounts.Name.Create)]
public async Task<IHttpActionResult> Create(CreateUserBindingModel createUserBindingModel)
{
IHttpActionResult result;
var memberNameExists = UserManager.Users.Any(x => x.MemberName.ToLower() == createUserBindingModel.MemberName.ToLower());
if (!memberNameExists)
{
var applicationUser = new ApplicationUser
{
UserName = createUserBindingModel.Email,
Email = createUserBindingModel.Email,
FirstName = createUserBindingModel.FirstName,
LastName = createUserBindingModel.LastName,
Company = createUserBindingModel.Company,
Location = createUserBindingModel.Location,
PhoneNumber = createUserBindingModel.PhoneNumber,
MemberName = createUserBindingModel.MemberName,
LastLoginDate = SqlDateTime.MinValue.Value,
CreateDate = DateTime.Now,
CreatedBy = User.Identity.GetUserId(),
UpdateDate = DateTime.Now,
UpdatedBy = User.Identity.GetUserId(),
TwoFactorEnabled = createUserBindingModel.TwoFactorEnabled,
SecurityResetRequired = true,
PasswordExpirationDate = DateTime.Now.AddDays(Convert.ToDouble(ConfigurationManager.AppSettings["PasswordExpirationDays"]))
};
if (!string.IsNullOrEmpty(createUserBindingModel.AvatarBase64))
{
var avatarBytes = Convert.FromBase64String(createUserBindingModel.AvatarBase64);
var resizedAvatarBytes = ImageResizer.ResizeImage(avatarBytes, _avatarWidth, _avatarHeight);
applicationUser.UserAvatar = new ApplicationUserAvatar
{
Avatar = resizedAvatarBytes
};
}
var generatedPassword = PasswordGenerator.GenerateStrongPassword(10, 10);
var identityResult = await UserManager.CreateAsync(applicationUser, generatedPassword);
if (identityResult.Succeeded)
{
await UserManager.AddToRolesAsync(applicationUser.Id, createUserBindingModel.Roles.ToArray());
var token = await UserManager.GenerateEmailConfirmationTokenAsync(applicationUser.Id);
var callbackUri = string.Format("{0}?userId={1}&token={2}", createUserBindingModel.EmailConfirmationCallbackUri, applicationUser.Id, HttpUtility.UrlEncode(token));
await UserManager.SendEmailAsync(applicationUser.Id, Email.Confirmation.Subject, string.Format(Email.Confirmation.Body, string.Format("{0} {1}", applicationUser.FirstName, applicationUser.LastName), callbackUri, generatedPassword, _settings.AccessTokenExpirationTimeSpan.TotalHours));
var userUrl = new Uri(Url.Link(Routes.Accounts.Name.Get, new { id = applicationUser.Id }));
var roles = await UserManager.GetRolesAsync(applicationUser.Id);
var contract = _accountsMapper.ToContract(applicationUser, roles);
result = Created(userUrl, contract);
}
else
{
result = GetErrorResult(identityResult);
}
}
else
{
ModelState.AddModelError(string.Empty, "Member Name already exists!");
result = BadRequest(ModelState);
}
return result;
}
Once the email is generated the UI has following JS angular code which gets executed and the provide the userid and token to service.
Angular JS code:
angular.module('confirmEmailModule').factory('confirmEmailFactory', function ($http) {
var factory = {};
factory.confirmEmail = function(userId, token) {
var encodedToken = encodeURIComponent(token);
var uri = '/identity/api/accounts/confirmemail?userId=' + userId + '&token=' + token;
return $http.post(uri);
}
return factory;
});
and the Service is :
[AllowAnonymous]
[HttpPost]
[Route(Routes.Accounts.Template.ConfirmEmail, Name = Routes.Accounts.Name.ConfirmEmail)]
public async Task<IHttpActionResult> ConfirmEmail([FromUri] string userId, [FromUri] string token)
{
//var decodedToken = HttpUtility.UrlDecode(token);
var identityResult = await UserManager.ConfirmEmailAsync(userId, token);
var result = identityResult.Succeeded ? StatusCode(HttpStatusCode.NoContent) : GetErrorResult(identityResult);
return result;
}
Please advice.
I found the solution to this issue. I am posting it if somebody faced the same issue. In my case the services and web API were on different servers. Different machine keys caused this issue. So I generated the machine key for my Web application and posted the same machine key in web.config file of Identity service. After that it worked. For more information on generating machine key, following link is helpful.
http://gunaatita.com/Blog/How-to-Generate-Machine-Key-using-IIS/1058
This is what worked for me. Hope it helps out;
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return RedirectToAction("employees", "home");
}
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"The User ID {userId} is invalid";
return View("NotFound");
}
var result = await userManager.ConfirmEmailAsync(user, Uri.EscapeDataString(token));
if (result != null)
{
user.EmailConfirmed = true;
await userManager.UpdateAsync(user);
return View();
}
}

Entity Framework - how to save entity without saving related objects

In my Entity Framework, I have three related entities 'Client', 'ClientAddress' and 'LookupAddressType'. "LookupAddressType" is a master class specifying the type of available address type, like business address, residential address etc. ClientAddress depend on LookupAddresstype and Client. While saving a Client entity with relevant ClientAddress data, i'm getting following error.
"Violation of PRIMARY KEY constraint 'PK_LookupAddressType'. Cannot
insert duplicate key in object 'dbo.LookupAddressType'. The statement
has been terminated.
I do not need LookupAddressType to be inserted. Here I just need the relevant lookupAddressTypeId to be inserted in clientAddress entity.
The Saving code is like this:
Add(Client);
_objectContext.SaveChanges();
how can i do this?
The Load Code is below:
private void LoadClientDetails(EFEntities.Client _Client)
{
EFEntities.LookupClientStatu clientStatus;
var clientAddressList = new List<ClientAddress>();
if (_Client == null)
{
return;
}
//Assign data to client object
_Client.ClientName = rtxtName.Text;
_Client.Alias = rtxtAlias.Text;
_Client.ClientCode =Int32.Parse(rtxtClientCode.Text);
_Client.TaxPayerID = rtxtTaxPayerId.Text;
if (rcboStatus.SelectedIndex != 0)
{
clientStatus = new EFEntities.LookupClientStatu
{
ClientStatusID = (Guid) (rcboStatus.SelectedValue),
ClientStatusDescription = rcboStatus.Text
};
_Client.LookupClientStatu = clientStatus;
}
//_Client.Modified = EnvironmentClass.ModifiedUserInstance.Id;
_Client.EffectiveDate = rdtEffectiveDate.Value;
if (rdtExpDate.Value != rdtExpDate.MinDate)
{
_Client.ExpirationDate = rdtExpDate.Value;
}
else
{
_Client.ExpirationDate = null;
}
_Client.StartDate = DateTime.Now;
EFEntities.ClientAddress clientAddress = null;
// Iesi.Collections.Generic.ISet<ClientAddress> clientAddress = new HashedSet<ClientAddress>();
foreach (var cAddress in _clientController.client.ClientAddresses)
{
clientAddress = cAddress;
break;
}
if (clientAddress == null)
{
clientAddress = new EFEntities.ClientAddress();
}
clientAddress.Address1 = rtxtClientAdd1.Text;
clientAddress.Address2 = rtxtClientAdd2.Text;
clientAddress.Address3 = rtxtClientAdd3.Text;
// Address type details
if (rcboClientAddType.SelectedIndex != -1)
{
clientAddress.LookupAddressType = new EFEntities.LookupAddressType
{
AddressTypeID = (Guid) (rcboClientAddType.SelectedValue),
AddressTypeDescription = rcboClientAddType.Text
};
//clientAddress.AddressType.Id = Convert.ToByte(rcboClientAddType.SelectedValue);
}
clientAddress.City = rtxtClientCity.Text;
clientAddress.Client = _Client;
\
_Client.ClientAddresses.Add(clientAddress);
}
Well I did this to the following lines of code to make it work.
if (rcboClientAddType.SelectedIndex != -1)
{
clientAddress.LookupAddressType = new EFEntities.LookupAddressType
{
AddressTypeID = (Guid) (rcboClientAddType.SelectedValue),
AddressTypeDescription = rcboClientAddType.Text
};
//clientAddress.AddressType.Id = Convert.ToByte(rcboClientAddType.SelectedValue);
}
I changed the above code into this
if (rcboClientAddType.SelectedIndex != -1)
{
//clientAddress.LookupAddressType = new EFEntities.LookupAddressType
// {
// AddressTypeID = (Guid) (rcboClientAddType.SelectedValue),
// AddressTypeDescription = rcboClientAddType.Text
// };
clientAddress.AddressTypeID = (Guid)(rcboClientAddType.SelectedValue);
}