How to store unique class instances in SQLite? - sqlite

I'm trying to store some information as a class instance in a SQLite table. The instances need to be unique in their DateTime property. I'm completely new to database programming, and I don't really understand how use SQLite in Xamarin. As new instances are created they need to update the existing instances in the table if they match in their DateTime property.
SQLiteAsyncConnection connection = new SQLiteAsyncConnection(App.FilePath);
await connection.CreateTableAsync<ModulInformationData>();
ModulInformationData data = new ModulInformationData();
data.InitModulInformation(modul);
int rows = 0;
try
{
rows = await connection.UpdateAsync(data);
}catch(Exception e)
{
Console.WriteLine("SQL Update failed " + e.Message);
}
Console.WriteLine("rows updated: " + rows);
if (rows == 0)
{
Console.WriteLine("before insert");
try
{
int key1 = await connection.InsertAsync(data);
Console.WriteLine("after insert: " + key1);
}catch (Exception e)
{
Console.WriteLine("SQL insert failed " + e.Message);
}
}
The ModulInformationData class
public class ModulInformationData
{
[PrimaryKey,AutoIncrement]
public int Id { get; set; }
[Unique]
public DateTime tidspunkt { get; set; }
other properties...
At the moment, I'm catching an error when inserting, but the message only says 'Constraint'. What can I do to make this work?

Do you want to achieve the result like this GIF。
First of all, you can create a class for the database CRUD.
public class NoteDatabase
{
readonly SQLiteAsyncConnection _database;
public NoteDatabase(string dbPath)
{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<Note>().Wait();
}
public Task<List<Note>> GetNotesAsync()
{
return _database.Table<Note>().ToListAsync();
}
public Task<Note> GetNoteAsync(int id)
{
return _database.Table<Note>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}
public Task<int> SaveNoteAsync(Note note)
{
if (note.ID != 0)
{
return _database.UpdateAsync(note);
}
else
{
return _database.InsertAsync(note);
}
}
public Task<int> DeleteNoteAsync(Note note)
{
return _database.DeleteAsync(note);
}
}
Then there is model class.
public class Note
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Text { get; set; }
[Unique]
public DateTime Date { get; set; }
public string Gender { get; set; }
}
If we want to add or update the data to the database, firstly, we should update the data to the instance, then we could just save this instance to the database like following save click event.
async void OnSaveButtonClicked(object sender, EventArgs e)
{
var note = (Note)BindingContext;
note.Date = DateTime.UtcNow;
note.Gender = (string)myPicker.SelectedItem;
await App.Database.SaveNoteAsync(note);
await Navigation.PopAsync();
}
Here is my demo. you can download it and refer to it.
https://github.com/851265601/databaseDemo

Related

How do I prevent a duplicate entry for a Create/Edit Functionality in ASP.NET Core w/ EF?

I am trying to prevent a user from creating a form with a FirstName, LastName, and DOB that match an entry in the database and editing a form to match an existing entry. If you could also lead me to how I can show an error when this happens, that would be awesome.
My Model:
public class MRegForm
{
public int MRegFormId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; } = string.Empty;
[Display(Name = "Last Name")]
public string LastName { get; set; } = string.Empty;
public DateTime DOB { get; set; }
[I tried Index attribute. It did not work for me. I was able to create new duplicate forms with no issues.
[Index(nameof(FirstName), nameof(LastName), nameof(DOB), IsUnique = true)]
public class MRegForm
{
I also tried this. Same thing.
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
base.OnModelCreating(modelbuilder);
modelbuilder.Entity<MRegForm>()
.HasIndex(x => new { x.FirstName, x.LastName, x.DOB})
.IsUnique();
}
public DbSet<MRegForm> MRegForm { get; set; } = default!;
I think that there is maybe a way to prevent this in the OnPostAsync()
This is my create OnPostAsync():
public async Task<IActionResult> OnPostAsync()
{
MRegForm.CreatorId = UserManager.GetUserId(User);
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Create);
if (isAuthorized.Succeeded == false)
return Forbid();
Context.MRegForm.Add(MRegForm);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
This is my Edit OnPostAsync():
public async Task<IActionResult> OnPostAsync(int id)
{
var mRegForm = await Context.MRegForm.AsNoTracking().SingleOrDefaultAsync(m => m.MRegFormId == id);
if (mRegForm == null)
return NotFound();
MRegForm.CreatorId = mRegForm.CreatorId;
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Update);
if (isAuthorized.Succeeded == false)
return Forbid();
MRegForm.Status = mRegForm.Status; // the Status is the current Status - Do Not Reset
Context.Attach(MRegForm).State = EntityState.Modified;
try
{
await Context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MRegFormExists(MRegForm.MRegFormId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MRegFormExists(int id)
{
return (Context.MRegForm?.Any(e => e.MRegFormId == id)).GetValueOrDefault();
}
}
You can try to download the entity from the database if exists and make changes to it or creating new one if not exist.
Your clients can always make new MRegForm in the form, but you add or update in the back and. Bether option will be to pass the existing MRegForm to the form and the client see and change all props he need.
public async Task AddOrUpdate(MRegForm input)
{
var mRegForm = await Context.MRegForm
.FirstOrDefaltAsync(x => x.FirstName == input.FirstName && x.LastName == input.LastName && x.DOB == input.YourDate);
if(mRegForm != null)
{
//Make changes on mRegForm
mRegForm.SomeProp = input.SomeProp,
...
}
else
{
var newMRegForm = new MRegForm
{
//Set all props you need
}
await this.Context.AddAsync(newMRegForm );
}
await this.Context.SaveCangesAsync();
}

Xamarin, how can i access a specific column property in sqlite so i can add the values?

I know how to read or show all data inserted in the database but I don't know how to query a specific data. In the my database is in Post.cs it will store date/time and get 2 datas from user.
so like this: enter image description here
Column1(ID), Column2(Date), (column3(rain1), column4(rain2).
1 12/13/2019 21 22
2 12/16/2019 21 22
3 12/16/2019 11 12
I want to do: if (rain1 && rain2) have the same date. I would like to add 2data from rain1 which is 21+11 and save the total to rain1total=32 then rain2total would be 34. I don't know where to start
here is Post.cs
namespace listtoPDF.Model
{
//Post table, user posting drainvolume
//this is the source of Binding
public class Post: INotifyPropertyChanged
{
private double? rain1Vol;
private double? rain2Vol;
//from settingspage to show up in history
string rain1lbl = Settings.Drain1LocationSettings;
string rain2lbl = Settings.Drain2LocationSettings;
//ID primary key that we will autoincrement
//These are columns ID, drain1 to 8 so 9 columns
//These are binding source for Historypage
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public static bool showLabel { get; set; } //public class model
public string rain1Lbl
{
get => rain1lbl;
set => rain1lbl= Settings.Drain1LocationSettings;
}
public string rain2Lbl
{
get => rain2lbl;
set => rain2lbl= Settings.Drain2LocationSettings;
}
public string CDateTime { get; set; }
[MaxLength(3)]
public double? rain1vol
{
get { return rain1Vol; }
set
{
rain1Vol = value;
RaisePropertyChanged("rain1vol");
}
}
[MaxLength(3)]
public double? rain2vol
{
get { return rain2Vol; }
set
{
rain2Vol = value;
RaisePropertyChanged("rain2vol");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
here is the main that will list data entered with date
namespace listtoPDF
{
public partial class MainPage : ContentPage
{
List<Post> posts;
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation);
conn.CreateTable<Post>();
posts = conn.Table<Post>().ToList();
postListView.ItemsSource = posts;
conn.Close();
//right click quickwatch
}
void addbuttonHandle_Clicked(object sender, EventArgs args)
{
Navigation.PushModalAsync(new AddRainsPage());
}
private void GetValues()
{
//Initiate SQLite connection
//Create table with class
//Get the values in table
//close the line
SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation);
conn.CreateTable<Post>();
posts = conn.Table<Post>().ToList();
conn.Close();
}
}
Here is the page where i need query the add a certain column so i can add the data associated to it. So far it's connecting to the sqlite.
namespace listtoPDF
{
public partial class selecteddataPage : ContentPage
{
List<Post> posts;
public selecteddataPage()
{
InitializeComponent();
}
private void GetValues()
{
//Initiate SQLite connection
//Create table with class
//Get the values in table
//close the line
SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation);
conn.CreateTable<Post>();
posts = conn.Table<Post>().ToList();
**if (Post.rain1 values have the same date )
{
totalrain1= then add data that have same date
}
if (Post.rain2 values have the same date )
{
totalrain1= then add data that have same date
}**
conn.Close();
}
}
}

sqlite-net-extensions -- Create Table Async

My problem is that I'm using the CreateTableAsync method and is always returning "0". I researched and saw that this return is a mistake.
I wanted to know what I'm doing wrong.
Class Service Table:
public class SqliteTable
{
private readonly SqliteWrapper _sqliteWrapper;
public SqliteTable()
{
_sqliteWrapper = new SqliteWrapper();
}
private async Task<bool> CheckIfExistTable<T>() where T : new()
{
var connection = _sqliteWrapper.OpenDatabase();
try
{
var result = await connection.Table<T>().CountAsync();
return result.Equals(0);
}
catch (Exception e)
{
Logs.Logs.Error($"Error get count table {typeof(T).Name}: {e.Message}");
return false;
}
}
public async void CreateTable<T>() where T : new()
{
var connection = _sqliteWrapper.OpenDatabase();
if (await CheckIfExistTable<T>())
{
Logs.Logs.Info($"This table {typeof(T).Name} was created");
return;
}
var createTableResult = await connection.CreateTableAsync<T>();
var value = createTableResult.Results.Values.FirstOrDefault();
if (value.Equals(1))
{
Logs.Logs.Info($"Create table {typeof(T).Name}");
}
else
{
throw new Exception($"Error create table {typeof(T).Name}");
}
}
}
I create a Class Model Login. which would be the object to create the database.
public class Login
{
public Login()
{
}
public Login(string user, string password)
{
User = user;
Password = password;
}
public Login(int id, string user, string password)
{
Id = id;
User = user;
Password = password;
}
[PrimaryKey, AutoIncrement, Column("login_id")]
public int Id { get; set; }
[Unique, NotNull, Column("login_user")]
public string User { get; set; }
[NotNull, Column("login_password")] public string Password { get; set; }
}
I create Class CreateTableAsync. Which would be to intanciar the SqliteTable, to call the method CreateTable sending the object to create the database:
protected override void OnStart()
{
try
{
var sqliteTable = new SqliteTable();
sqliteTable.CreateTable<Login>();
}
catch (Exception e)
{
Logs.Logs.Error($"Error init application: {e.Message}");
}
}
Can someone help me?

ASP.NET web application + Entity Framework 4.0 with Concurrency Check

I have an entity like this:
public class Player
{
[Required]
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
public string NativeCountry { get; set; }
[ConcurrencyCheck]
public DateTime LastModified { get; set; }
public virtual int TeamId { get; set; }
//navigational property
public virtual Team Team { get; set; }
public virtual ICollection<Tournament> Tournaments { get; set; }
}
this is how i configure Player entity:
public PlayerConfiguration()
{
Property(e => e.Id).IsRequired();
Property(e => e.FirstName).IsRequired().IsConcurrencyToken(true);
Property(e => e.NativeCountry).IsOptional();
Property(e => e.LastModified).IsOptional().IsConcurrencyToken(true);
HasRequired(e => e.Team).WithMany(s => s.Players).HasForeignKey(f => f.TeamId);
}
overridden OnModelCreating
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Configuration.ValidateOnSaveEnabled = false;
Configuration.LazyLoadingEnabled = true;
modelBuilder.Configurations.Add(new PlayerConfiguration());
modelBuilder.Configurations.Add(new TeamConfiguration());
modelBuilder.Configurations.Add(new TournamentConfiguration());
modelBuilder.Entity<Player>().ToTable("Player");
modelBuilder.Entity<Team>().ToTable("Team");
modelBuilder.Entity<Tournament>().ToTable("Tournament");
base.OnModelCreating(modelBuilder);
}
somewhere I do this to update a player:
db.Entry<Player>(player).State = System.Data.EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
}
when I try to update any given player at the same time, using two browsers, nothing happens. I don't want to use a TimeStamp annotation, because it will cost me one extra
column. How can I use the existing DateTime LastModified column to track concurrency.
I even tried making FirstName (and others) as ConcurrencyToken, but again nothing happened.
How does this [ConcurrencyCheck] work in asp.net web application.??
please help..
It looks like your code doesn't change LastModified property so you are using still the same concurrency token. That is a reason why people use Timestamp column because timestamp is automatically handled by database and you don't have to deal with it.
To use concurrency token your entity must follow very strict rules.
Your entity must hold old value of concurrency token (in case of ASP.NET it requires round tripping concurrency token to client and receiving it back with modified data).
If you don't use database generated concurrency token (timestamp or row version) you must set it manually each time you are going to change the record
If you work with detached entities the new token can be set only ofter you attach the entity to the context otherwise you will get exception every time you try to save updated data
Here is sample code to validate that concurrency checking works:
class Program
{
static void Main(string[] args)
{
using (var context = new Context())
{
context.Database.Delete();
context.Database.CreateIfNotExists();
context.Players.Add(new Player { FirstName = "ABC", LastName = "EFG", NativeCountry = "XYZ", LastModified = DateTime.Now});
context.SaveChanges();
}
using (var context = new Context())
{
var player = context.Players.First();
// Break here, go to database and change LastModified date
player.LastModified = DateTime.Now;
// If you changed LastModified date you will get an exception
context.SaveChanges();
}
}
}
this is what I did to make it work:
Player entity:
[ConcurrencyCheck]
public DateTime LastModified { get; set; }
in my code, onLoad of the form, called this function:
private static DateTime TimeStamp;
protected void LoadData()
{
Player player = new PlayerRepository().First(plyr => plyr.Id == Id);
if (player != null)
{
TimeStamp = player.LastModified;
//rest of the code
}
}
and upon click of the save button i.e. while updating entity:
protected void btnSave_Click(object sender, EventArgs e)
{
var playerRepo = new PlayerRepository();
var teamRepo = new TeamRepository();
if (Page.IsValid)
{
Player player;
if (this.Id == 0)//add
{
player = new Player();
//rest of the code
}
else //edit
player = playerRepo.First(p => p.Id == Id);
player.LastModified = DateTime.Now;
//custom logic
if (this.Id == 0) // add
{
playerRepo.Add(player);
playerRepo.SaveChanges();
}
else // edit
{
try
{
playerRepo.OriginalValue(player, "LastModified", TimeStamp);
playerRepo.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
//custom logic
}
}
}
and OriginalValue in the AbstractRepository is this:
public void OriginalValue(TEntity entity, string propertyName, dynamic value)
{
Context.Entry<TEntity>(entity).OriginalValues[propertyName] = value;
}
so explicitly i had to change the OriginalValue of the ConcurrencyCheck marked column to the older one.

How to consume this web service?

This is my first time creating a web service. I am not sure if my implementation is incorrect, but I am trying to use much like a class. The problem is that when I am trying to consume I am getting confused and not being able to set the values of the properties.
here is the web service.
public class Service1 : System.Web.Services.WebService
{
private bool _isUserActive { get; set; }
private bool _isCredentialValid { get; set; }
public string email { get; set; }
public string pass { get; set; }
public int customerID { get; set; }
[WebMethod]
public bool VerifyUserCredential()
{
bool result = false;
PURLDataContext purl = new PURLDataContext();
try
{
var res = purl.Sel_User(email.ToLower(), pass);
if (res != null)
result = true;
_isUserActive = true;
_isCredentialValid = true;
}
catch (Exception ex)
{
if (ex.Message == "Account is inactive, please contact your administrator!")
{
_isUserActive = false;
_isCredentialValid = false;
}
else
_isCredentialValid = false;
//Invalid credentials.
}
return result;
}
[WebMethod]
public ArrayList retrieveCustomerInfo()
{
ArrayList customerInfo = new ArrayList();
string validate = "Please Validate";
if (_isCredentialValid)
{
PURLDataContext purl = new PURLDataContext();
var customer = purl.Sel_Recipient(customerID);
foreach (var c in customer)
{
customerInfo.Add(c);
}
}
else
customerInfo.Add(validate);
return customerInfo;
}
}
Here is what I am trying to do to consume.
PURLServices.Service1SoapClient webserv = new Service1SoapClient();
bool result;
ArrayOfAnyType array = new ArrayOfAnyType();
webserv.email = "email#email.com";
webserv.pass = "pass";
webserv.customerID = 12345;
result = webserv.VerifyUserCredential();
array = webserv.retrieveCustomerInfo();
Thank you for any help/
You do not want to try to use properties like this. Your method should look more like this:
public bool VerifyUserCredential(string userName, string password)
{
// method body here
}
Probably you would want to return an access token of some sort that the server will cache. This can then be passed into other methods to show that the user is valid.

Resources