Xamarin SQLite - ManyToMany relationship - c#

I have 4 classes that i deal with in my Xamarin.Forms project: Users, User_Profiles, Medicines and Medicne_Incident.
Business rules:
Each User has 1 User_Profile (User profile is basically a detailed description of that user, User_Profile inherits from User)
There is a ManyToMany relation between Users and the medicines, and so a bridge table is created: Medicine_Incident
A user can have multiple Medicine_Incidents, but that incident is unique to the user.
I have no idea how to connect the dots (i.e., to have a stable relationship diagram for them).
Below are the class definitions for each of them:
User:
public class User
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public bool ProfileComplete { get; set; }
public User()
{
ProfileComplete = false;
Username = "";
Email = "";
Password = "";
}
public User(string email, string Password)
{
this.Email = email;
this.Password = Password;
}
}
User_Profiles:
public class User_Profiles : User
{
public string Gender { get; set; }
public string Country { get; set; }
public string Address { get; set; }
public string Province { get; set; }
public string Postal { get; set; }
public User_Profiles()
{
//Medicine = new List<Medicine>();
}
//?Do we need this?
public User_Profiles(string email, string Password)
{
//Medicine = new List<Medicine>();
this.Username = "";
//this.Dob = DateTime.Today;//Probably wrong to do
this.Gender = "";
this.Country = "";
this.Address = "";
this.Province = "";
this.Postal = "";
//Password/Email for User
this.Email = email;
this.Password = Password;
}
}
Medicine:
public class Medicine //: User_Profiles
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Medicine_Name { get; set; }
public string Medicine_Desc { get; set; }
public Medicine()
{
Medicine_Name = "";
Medicine_Desc = "";
//Medicine_Incident = new List<Medicine_Incident>();
}
}
Medicine_Incident:
public class Medicine_Incident : Medicine
{
public virtual Medicine Medicine { get; set; }
public virtual User_Profiles User_Profiles { get; set; }
public DateTime Time { get; set; }
public int Dosage { get; set; }
public Medicine_Incident()
{
// (1/1/0001 12:00:00 AM)
Dosage = 0;
}
}
I do my database querying in a file called "UserDatabase.cs":
public class UserDatabase
{
readonly SQLiteAsyncConnection database;
public UserDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<User_Profiles>().Wait();
database.CreateTableAsync<User>().Wait();
database.CreateTableAsync<Medicine>().Wait();
database.CreateTableAsync<Medicine_Incident>().Wait();
}
public void SaveProfileAsync(User_Profiles user)
{
database.InsertAsync(user);
}
public Task<User_Profiles> GetProfileAsync(string email, string pass)
{
return database.Table<User_Profiles>().Where(i => (i.Email.Equals(email)) && (i.Password.Equals(pass))).FirstOrDefaultAsync();
}
public void SaveMedicineAsync(Medicine med)
{
database.InsertAsync(med);
}
public void SaveMedicineIncidentAsync(Medicine_Incident med_inc)
{
database.InsertAsync(med_inc);
}
public Task<Medicine_Incident> GetMedicineIncidentAsync(string email, string medName, DateTime time)
{
return database.Table<Medicine_Incident>().Where(i => (i.User_Profiles.Email.Equals(email)) && (i.Medicine.Medicine_Name.Equals(medName)) && (i.Time.Equals(time))).FirstOrDefaultAsync();
}
}
Now the main thing is, i know i am doing it wrong, but how can i rectify it? Trust me when i say i spent time solving this and finding solution to this online.
NOTE:
- I am using SQLite.
- I have the SQLite extension in the project.

Sorry for the poor english
There is a lot of problems here. I guess you should read this and this article to help you learn how to model class/database in your code, but here is some tips:
 
Inheritance is used to extend a class (for example: Human inherits
from mammal that inherits from animal that inherits from living
being)
Your User_Profile class can have a public List Users (that
must be ignored by your SQLite database) and a Id [PrimaryKey]
Your User class, the IdUser_Profile (this will define which profile
each user have). It can be reffered to your UserProfile class...
I hope that it helps you.
Edit
You're right. This will not help you. I let it pass some details about your scope. Here is a more usefull information:
The only problem I see is the Medicine_Incident class. I would do so:
public class Medicine_Incident
{
[SQLite.Net.Attributes.PrimaryKey, SQLite.Net.Attributes.AutoIncrement]
public int Id { get; set; }
public int IdMedicine { get; set; }
[SQLite.Net.Attributes.Ignore]
public Medicine Medicine { get; set; }
public int IdUser_Profiles { get; set; }
[SQLite.Net.Attributes.Ignore]
public User_Profiles User_Profiles { get; set; }
public DateTime Time { get; set; }
public int Dosage { get; set; }
public Medicine_Incident()
{
// (1/1/0001 12:00:00 AM)
Dosage = 0;
}
}
And in your database query:
public class UserDatabase
{
readonly SQLiteAsyncConnection database;
public UserDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<User_Profiles>().Wait();
// Don't do that (Your User's attribute will be persisted within your User_Profiles table): database.CreateTableAsync<User>().Wait();
database.CreateTableAsync<Medicine>().Wait();
database.CreateTableAsync<Medicine_Incident>().Wait();
}
public void SaveProfileAsync(User_Profiles user)
{
database.InsertAsync(user);
}
public Task<User_Profiles> GetProfileAsync(string email, string pass)
{
return database.Table<User_Profiles>().Where(i => (i.Email.Equals(email)) && (i.Password.Equals(pass))).FirstOrDefaultAsync();
}
public void SaveMedicineAsync(Medicine med)
{
database.InsertAsync(med);
}
public void SaveMedicineIncidentAsync(Medicine_Incident med_inc)
{
database.InsertAsync(med_inc);
}
public Task<Medicine_Incident> GetMedicineIncidentAsync(string email, string medName, DateTime time)
{
var user = database.Table<User_Profiles>().Where(u => u.Email == email).FirstOrDefault();
var medicine = database.Table<Medicine>().Where(m => m.Medicine_Name == medName).FirstOrDefault();
var medInc = database.Table<Medicine_Incident>().Where(mi => mi.IdUser_Profiles == user.Id && mi.IdMedicine == medicine.Id).FirstOrDefault();
medInc.User = user;
medInc.Medicine = medicine;
return medInc;
}
}
SQLite doesn't handle relationships between classes when querying. To do this, use the SQLite Net Extensions.
I hope it helps you.

Related

Entity Framework N- Layer no binding with datagridview

I have a layer of Data like this to bring me all Admins:
public List<Datos_Admin> SelectAllAdmin()
{
return (from u in contexto.Datos_Admin select u).ToList() ;
}
In my layer of Entities have this:
public class EDatos_Admin
{
public int Id_Admin { get; set; }
public string User_Name { get; set; }
public char Password { get; set; }
public string Nombre { get; set; }
public string Ap_Paterno { get; set; }
public string Ap_Materno { get; set; }
public DateTime Fecha_Alta { get; set; }
public DateTime Fecha_Modificacion { get; set; }
public int UserAdmin_Modificacion { get; set; }
public bool Activo { get; set; }
public EDatos_Admin(int Id_Admin, string User_Name, char Password, string Nombre, string Ap_Paterno, string Ap_Materno, DateTime Fecha_Alta, DateTime Fecha_Modificacion, int UserAdmin_Modificacion, bool Activo)
{
this.Id_Admin = Id_Admin;
this.User_Name = User_Name;
this.Password = Password;
this.Nombre = Nombre;
this.Ap_Paterno = Ap_Paterno;
this.Ap_Materno = Ap_Materno;
this.Fecha_Alta = Fecha_Alta;
this.Fecha_Modificacion = Fecha_Modificacion;
this.UserAdmin_Modificacion = UserAdmin_Modificacion;
this.Activo = Activo;
}
public EDatos_Admin()
{
// TODO: Complete member initialization
}
}
And in my Bussines layer have this:
public EDatos_Admin SeleccionaAllDatos_Admin()
{
foreach (var n in contexto.SelectAllAdmin())
{
listDatosAdmin = new EDatos_Admin()
{
Id_Admin = n.Id_Admin,
User_Name = n.User_Name,
};
}
return listDatosAdmin;
}
In the datagridview only I want show me Id_Admin and User_Name but it doesn't. And when I review in debugging I can see the data but the datagridview doesn't work.
And I call the method SeleccionaAllDatos_Admin in my Bussines layer like this in the load form
dataGridView1.DataSource = new NDatos_Admin().SeleccionaAllDatos_Admin();
How can fixed?
Thanks

Display new table in the database MVC4 asp.net

I am cluless, i dont know why my database its showing in the deafult connection in the database , I am programing in C# MVC4 asp.net using razor technology,
here is my UserAccountContext
public class UserAccountContext : DbContext
{
public UserAccountContext()
: base("DefaultConnection")
{
}
public DbSet<UserAccount> UserAccounts { get; set; }
// Methods...
public DbSet<UserProfile> UserProfiles { get; set; }
// Methods...
public IQueryable<UserAccount> GetAll()
{
IQueryable<UserAccount> query = this.UserAccounts;
return query;
}
public UserAccount GetSingle(string _UserName)
{
UserAccount Query = this.GetAll().FirstOrDefault(x => x.UserName == _UserName);
return Query;
}
public void Save(UserAccount _UserAccount)
{
if (_UserAccount.ID == 0)
{
this.UserAccounts.Add(_UserAccount);
this.SaveChanges();
}
else
{
this.SaveChanges();
}
}
}
And here is my UserAccount Model
public class UserAccount
{
// Attributes turns into fields in the database
// These attributes persist in the database
[Key]
public int ID { get; set; }
public int UserId { get; set; }
public string UserName { get; set; }
public string WhoVoted { get; set; }
public double NumberOfVotes { get; set; }
// The following attributes will not persist in the DB
public string UserErrorMessage = string.Empty; // Volatile Variable
// Methods...
public UserAccount() { } //Constructor... This is needed to create the DB Table
public UserAccount(int _UserId, string _UserName) // Other Constructor
{
this.ID = 0;
this.UserId = _UserId;
this.UserName = _UserName;
this.NumberOfVotes = 0.0;
this.WhoVoted = string.Empty;
}
}
I have everything, i dont know how to deal with the database , Lookking forward for any help. Thank you in advance!

SQLite query failing but only when offline

I have a simple SQLite method which returns a class given two parameters - the type T and the parameter value to be searched
public T GetSingleObject<T>(string id) where T:IIdentity, new()
{
lock (dbLock)
{
using (var sqlCon = new SQLiteConnection(DBPath))
{
sqlCon.Execute(Constants.DBClauseSyncOff);
sqlCon.BeginTransaction();
string sql = string.Format("SELECT * FROM {0} WHERE id=\"{1}\"", GetName(typeof(T).ToString()), id);
var data = sqlCon.Query<T>(sql);
return data[0];
}
}
}
I also have another method that takes 3 parameters, T, the parameter to be seated and the value to search for
public T GetSingleObject<T>(string para, string val) where T:IIdentity, new()
{
lock (dbLock)
{
using (var sqlCon = new SQLiteConnection(DBPath))
{
sqlCon.Execute(Constants.DBClauseSyncOff);
sqlCon.BeginTransaction();
string sql = string.Format("SELECT * FROM {0} WHERE {1}=\"{2}\"", GetName(typeof(T).ToString()), para, val);
var data = sqlCon.Query<T>(sql).FirstOrDefault();
return data;
}
}
}
These methods work without an issue and return the values expected, but with one caveat - there is a single table which works fine when the phone has a connection, but not when it's in aeroplane mode. The table has nothing more in it than strings, doubles, ints, DateTime and bool values.
GetName is a simple method that removes the assembly bits and pieces and leave just the classname
private string GetName(string name)
{
var list = name.Split('.').ToList();
if (list.Count == 1)
return list[0];
var last = list[list.Count - 1];
return last;
}
I have tried every way I can think of to access this table - including a straight
sqlCon.ExecuteScalar<MyClass>("SELECT * FROM MyClass");
and nothing offline, perfect data online.
The class itself looks like this
using System;
using System.Runtime.Serialization;
using System.Collections.Generic;
using SQLite;
namespace Models
{
public class MyClass : IIdentity
{
[PrimaryKey]
public string id { get; set; }
public string home_id { get; set; }
public string username { get; set; }
public string password { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string email { get; set; }
public bool syncenabled { get; set; }
public string tradingname { get; set; }
public string address { get; set; }
public string town { get; set; }
public string state { get; set; }
public string country { get; set; }
public string securitystamp { get; set; }
public string password_question { get; set; }
public string password_answer { get; set; }
public string mobileServiceAuthenticationToken { get; set; }
public string providerUserKey { get; set; }
public string mobiledeviceid { get; set; }
public DateTime last_login { get; set; }
public DateTime __createdAt { get; set; }
public DateTime __updatedAt { get; set; }
public string user_type { get; set; }
public bool is_deleted { get; set; }
public bool account_enabled { get; set; }
public string subscription_id { get; set; }
[IgnoreDataMember, Ignore]
public List<MyUsers> UserModules { get { return AppDelegate.Self.DBManager.GetListOfObjects<MyUsers>("user_id", id); } }
public override string ToString()
{
return string.Format("[MyClasss: id={0}, home_id={1}, username={2}, password={3}, firstname={4}, lastname={5}, email={6}, syncenabled={7}, tradingname={8}, address={9}, town={10}, state={11}, country={12}, securitystamp={13}, password_question={14}, password_answer={15}, mobileServiceAuthenticationToken={16}, providerUserKey={17}, mobiledeviceid={18}, last_login={19}, __createdAt={20}, __updatedAt={21}, user_type={22}, is_deleted={23}, account_enabled={24}, subscription_id={25}, UserModules={26}]", id, home_id, username, password, firstname, lastname, email, syncenabled, tradingname, address, town, state, country, securitystamp, password_question, password_answer, mobileServiceAuthenticationToken, providerUserKey, mobiledeviceid, last_login, __createdAt, __updatedAt, user_type, is_deleted, account_enabled, subscription_id, UserModules);
}
}
Nothing in there that I can see should cause an issue when offline. The query always returns null offline, data online.

Set Properties within base function and call function in child function inheritance

I am trying to refactor some old code and wanted to create more logical inheritance.
We have struct Custom Class which we have separated into (3) levels:
AccountView > Details > Full with inheritance. We set the properties of each one as needed.
After looking at the setters, we wanted to combine them into a single class 'SetAccountProp' with methods that set the properties.
We have the 'CustomerBaseView' where we pass in Models ACCOUNT data which works.
Now for the CustomerDetailView pass the same Model ACCOUNT data, but we would like to fill the properties of 'CustomerBaseView' use function 'CustomerBaseView' then fill the details.
Also, for CustomerFullView pass the Model ACCOUNT data, and fill the properties of 'CustomerBaseView' THEN 'CustomerBaseView' and then the remaining fields for CustomerFullView.
How can I call and fill the 'CustomerBaseView' within the 'CustomerDetailView' function? Do I initialize new AccountsView(); in each function?
Not sure how to finish up the refactor without repeating the:
// -- CustomView <--- replace with func?
view.Email = data.Email;
view.Active = data.Active;
view.FirstName = data.FirstName;
view.LastName = data.LastName;
in the Details and Full functions.
CODE
namespace BLL.Presenters
{
public class AccountsView
{
public string Email { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Details : AccountsView
{
public bool Administrator { get; set; }
public DateTime? LastLogin { get; set; }
}
public class Full : Details
{
public Guid ID { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public string FullName { get; set; }
}
public class SetAccountProp
{
public static AccountsView CustomerBaseView(Account data)
{
var view = new AccountsView();
view.Email = data.Email;
view.Active = data.Active;
view.FirstName = data.FirstName;
view.LastName = data.LastName;
return view;
}
public static Details CustomerDetailView(Account data)
{
var view = new Details();
// -- CustomView <--- replace with func?
view.Email = data.Email;
view.Active = data.Active;
view.FirstName = data.FirstName;
view.LastName = data.LastName;
// -- Details
view.Administrator = data.Administrator;
view.LastLogin = data.LastLogin;
return view;
}
public static Full CustomerFullView(Account data)
{
var view = new Full();
// -- CustomView <--- replace with func?
view.Email = data.Email;
view.Active = data.Active;
view.FirstName = data.FirstName;
view.LastName = data.LastName;
// -- Details <--- replace with func?
view.Administrator = data.Administrator;
view.LastLogin = data.LastLogin;
// -- Full
view.ID = data.ID;
view.Created = data.Created;
view.Modified = data.Modified;
view.FullName = data.LastName + ", " + data.FirstName;
return view;
}
}
}
Using constructor chaining, you could have something like this:
Each constructor calls it's base class' constructor first, so you don't have to repeat code.
namespace BLL.Presenters
{
using System;
public class Account // dummy to make it compile
{
public string Email;
public bool Active;
public string FirstName;
public string LastName;
public bool Administrator;
public DateTime? LastLogin;
public Guid ID;
public DateTime Created;
public DateTime Modified;
}
public class AccountsView
{
public string Email { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AccountsView(Account data)
{
this.Email = data.Email;
this.Active = data.Active;
this.FirstName = data.FirstName;
this.LastName = data.LastName;
}
}
public class Details : AccountsView
{
public bool Administrator { get; set; }
public DateTime? LastLogin { get; set; }
public Details(Account data) : base(data)
{
this.Administrator = data.Administrator;
this.LastLogin = data.LastLogin;
}
}
public class Full : Details
{
public Guid ID { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public string FullName { get; set; }
public Full(Account data) : base(data)
{
this.ID = data.ID;
this.Created = data.Created;
this.Modified = data.Modified;
this.FullName = data.LastName + ", " + data.FirstName;
}
}
class Program
{
static void Main()
{
Console.WriteLine();
Console.ReadLine();
}
}
}
Why not something like this:
public class CustomerBase
{
public string Email { get; private set; }
public bool Active { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
protected void SetAccountInfo(Account account)
{
this.Email = account.Email;
this.Active = account.Active;
this.FirstName = account.FirstName;
this.LastName = account.LastName;
}
}
public class CustomerA : CustomerBase
{
public string IsAdmin { get; private set; }
public DateTime? LastLogin { get; private set; }
public void SetAccountInfo(Account account)
{
base.SetAccountInfo(account);
this.IsAdmin = account.IsAdmin;
this.LastLogin = account.LastLogin;
}
}
public class Account
{
//your properties
public string Email { get; set; }
public bool Active { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string IsAdmin { get; set; }
public DateTime? LastLogin { get; set; }
}
Or let the SetAccountInfo() return this
public CustomerA SetAccountInfo(Account account)
{
base.SetAccountInfo(account);
this.IsAdmin = account.IsAdmin;
this.LastLogin = account.LastLogin;
return this;
}

Using EF LazyLoading for initializing properties?

UPDATED: I understood I should not use a DbSet so I changed the implementation to an ICollection as suggested by Erenga
Please consider the following classes:
[Table("Tenant")]
public class Tenant : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("User")]
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
The first test creates a new Tenant and a new User and stores them in the appropriate tables.
[Test]
public void CreateNewUserForNewTenant()
{
var user = _applicationContext.Users.Create();
user.Name = "barney";
user.EmailAddress = "barney#flinstone.com";
var tenant = _applicationContext.Tenants.Create();
tenant.Name = "localhost";
tenant.Guid = Guid.NewGuid().ToString();
tenant.Users.Add(user); // NullReferenceException, I expected the EF would LazyLoad the reference to Users?!
_tenantRepository.Add(tenant);
_applicationContext.SaveChanges();
}
This test will fail on a NullReferenceException since the property Users is not initialized.
How should I change my code that I can rely on LazyLoading provided with EF?
There are 2 problems I see here.
As #SimonWhitehead mentioned, reference types are initialized as null by default. Lazy loading works only on entities created by EF. These are actually sub classes of your class that contain addtional logic to lazy load.
DbSet is not a collection type that is supported on entities. You need to change the type to ICollection, ISet, or IList.
Here's a working example
[Table("Tenant")]
public class Tenant : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("User")]
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
[Test]
public void CreateNewUserForNewTenant()
{
var user = _applicationContext.Users.Create();
user.Name = "barney";
user.EmailAddress = "barney#flinstone.com";
var tenant = _applicationContext.Tenents.Create();
tenant.Name = "localhost";
tenant.Guid = Guid.NewGuid().ToString();
tenant.Users = new List<User> { user };
_tenantRepository.Add(tenant);
_applicationContext.SaveChanges();
}
var tenant = new Tenant
{
Name = "localhost",
Guid = Guid.NewGuid().ToString(),
Users = new List<User> { user }
};
I think you were expecting something like this (not threadsafe):
[Table("Tenant")]
public class Tenant : IEntity
{
private DbSet<User> _users;
public int Id { get; set; }
public string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users
{
get
{
if (_users == null)
_users = new List<Users>();
return _users;
}
set { _users = value; }
}
}
I'm sure the Lazy<T> class could be utilised somehow too but I am not familiar with that class.

Categories

Resources