Xamarin forms SQLite relation - c#

I am going to create an app in Xamarin forms and starting with SQLite. I need to have unique list items for each Main item in the app.
For example, I am having a list with items. When I am selecting an item in the list a new page will pop up and display the items of that item.
So from my point of view I am in need of two SQLite tables with relations between.
This is the Main table with all profiles
[Table("Profiles")]
public class ProfileItems
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string ProfileName { get; set; }
public string ProfileRace { get; set; }
public string iconn = "icon.png";
public string ProfileIcon { get; set; }
public DateTime BDay { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<LoggItems> Loggs { get; set; }
}
This is the logg table for each Profile, which should be unique for each profile
[Table("Loggs")]
public class LoggItems
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
[ForeignKey(typeof(ProfileItems))]
public int ProfileId { get; set; }
}
Adding the items like this
public class ProfileDatabase
{
readonly SQLiteAsyncConnection database;
public ProfileDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<ProfileItems>().Wait();
database.CreateTableAsync<LoggItems>().Wait();
}
//Profile
public Task<List<ProfileItems>> GetProfileAsync()
{
return database.Table<ProfileItems>().ToListAsync();
}
public Task<ProfileItems> GetProfileAsync(int id)
{
return database.Table<ProfileItems>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
public Task<int> SaveProfileAsync(ProfileItems profileItems)
{
if (profileItems.Id != 0)
{
return database.UpdateAsync(profileItems);
}
else
{
return database.InsertAsync(profileItems);
}
}
public Task<int> DeleteProfileAsync(ProfileItems profileItems)
{
return database.DeleteAsync(profileItems);
}
//Logg
public Task<List<LoggItems>> GetLoggAsync()
{
return database.Table<LoggItems>().ToListAsync();
}
public Task<LoggItems> GetLoggAsync(int id)
{
return database.Table<LoggItems>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
public Task<int> SaveLoggAsync(LoggItems loggItems)
{
if (loggItems.Id != 0)
{
return database.UpdateAsync(loggItems);
}
else
{
return database.InsertAsync(loggItems);
}
}
public Task<int> DeleteLoggAsync(LoggItems loggItems)
{
return database.DeleteAsync(loggItems);
}
}
Both Logg and Profile list/tables do work but they do not have any relations between so the loggs show the same in all profile.
How should I do this?

How about to use Linq and join the relationships.
1.- First you have to add the namespace:
using System.Linq;
2.- Change the property in the class ProfileItems to be a IEnumerable
[OneToMany(CascadeOperations = CascadeOperation.All)]
public virtual IEnumerable<LoggItems> Loggs { get; set; }
3.- This is the method to join the loggs with the profile items.
var profiles = await GetProfileAsync();
var loggs = await GetLoggAsync();
var query = from p in profiles
join l in loggs on p.Id equals l.ProfileId into list
select new ProfileItems
{
Id = p.Id,
ProfileIcon = p.ProfileIcon,
ProfileName = p.ProfileName,
ProfileRace = p.ProfileRace,
BDay = p.BDay,
Loggs = list
};

I think you must add "virtual" keyword for enabling lazy loading.
[OneToMany(CascadeOperations = CascadeOperation.All)]
public virtual List<LoggItems> Loggs { get; set; }
And there's the "[InverseProperty]" to specify their related navigation property.
public class LoggItems
{
*
[ForeignKey(typeof(ProfileItems))]
[InverseProperty("Loggs")]
public int ProfileId { get; set; }
*
}

Related

How to return objects from many to many relation Db - entityframework

Im trying to return a List of products of a specific user by user Id, but that seems to not work
My Product class
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public List<User>? Users { get; set; }
}
My user class
public class User
{
[Key]
public Guid Id { get; set; }
[Required] [MaxLength(15)]
public string Username { get; set; }
[Required]
public string Name { get; set; }
public string Surname { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public List<Product>? Products { get; set; }
}
So im adding and Product to Db, this is working
And then im adding the product to Order by this method
Guid id is a user's id
public void AddProduct(Product product, Guid id)
{
var user = _context.Users.First(u => u.Id == id);
var p = _context.Products.First(p => p.Id == product.Id);
if (user.Products == null || p.Users == null)
{
user.Products = new List<Product>();
p.Users = new List<User>();
}
user.Products.Add(p);
p.Users.Add(user);
_context.SaveChanges();
}
And this also seems to work:
image of ProductUser table from db
So how can I return a List of Products which specific user have?
I've tried this:
private Order BuildOrder(Guid id)
{
var user = _context.Users.First(u => u.Id == id);
/*if (user.Products is null)
{
user.Products = new List<Product>();
}*/
var x = _context.Products.Where(p => p.Id == 1);
/*
var products = user.Products.ToList();*/
var order = new Order
{
Products = x.ToList()
};
return order;
But this is returning me null, like Adding Products is not working
Result of this method
Order class:
public class Order
{
public List<Product> Products { get; set; }
}
DbContext:
using Application.Api.Models;
using Microsoft.EntityFrameworkCore;
namespace Application.Api.Data;
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions<ApplicationContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseSerialColumns();
modelBuilder.Entity<User>(eb =>
{
eb.HasMany(u => u.Products).WithMany(p => p.Users);
});
}
public DbSet<User> Users { get; set; }
public DbSet<Product> Products { get; set; }
}
If that's not enough informations comment what I need to add
you need to explicitly include the Products for the user
var speceficUserWithProducts = context.Users.Include(u => u.Products).FirstOrDefault(u => u.Id == id);

Ef Core filtering included query using uow and repository

I started creating a role-based security system in my WinForm application so I began with Form navigation (Permission Navigation) and this is my entity's
public partial class User
{
public User()
{
UsersToRoles = new HashSet<UsersToRole>();
}
public string Login { get; set; } = null!;
public string PasswordUser { get; set; } = null!;
public string? FullName { get; set; }
public string? Email { get; set; }
public int Id { get; set; }
public virtual ICollection<UsersToRole> UsersToRoles { get; set; }
}
public partial class Role
{
public Role()
{
UsersToRoles = new HashSet<UsersToRole>();
PermissionNavigations = new HashSet<PermissionNavigation>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public virtual ICollection<UsersToRole> UsersToRoles { get; set; }
public virtual ICollection<PermissionNavigation> PermissionNavigations { get; set; }
}
public partial class UsersToRole
{
public int Id { get; set; }
public int IdUser { get; set; }
public int IdRole { get; set; }
public virtual Role IdRoleNavigation { get; set; } = null!;
public virtual User IdUserNavigation { get; set; } = null!;
}
public partial class Navigation
{
public Navigation()
{
PermissionNavigations = new HashSet<PermissionNavigation>();
}
public int Id { get; set; }
public string Page { get; set; } = null!;
public string Forms { get; set; } = null!;
public virtual ICollection<PermissionNavigation> PermissionNavigations { get; set; }
}
public partial class PermissionNavigation
{
public int Id { get; set; }
public int IdRole { get; set; }
public int IdNavigation { get; set; }
public virtual Navigation IdNavigationNavigation { get; set; } = null!;
public virtual Role IdRoleNavigation { get; set; } = null!;
}
This is my geniric GetAllIncluding method
public async Task<IEnumerable<T>> GetAllIncluding(params Expression<Func<T, object>>[] includeProperties)
{
try
{
IQueryable<T> query = dbSet;
foreach (Expression<Func<T, object>> includeProperty in includeProperties)
{
query = query.Include<T, object>(includeProperty);
}
return await query.ToListAsync();
}
catch (Exception ex)
{
throw new Exception($"{nameof(GetAllIncluding)} properties could not be included properly: {ex.Message}");
}
}
And this is how I use it in my PermissionNavigationService
public async Task<IEnumerable<PermissionNavigationDto?>>
GetAllPermissionNavigationDetailsByUserAsync(int idUser)
{
var permissionNavigation = await unitOfWork.PermissionNavigations.GetAllIncluding(
x => x.IdNavigationNavigation,
x => x.IdRoleNavigation,
x => x.IdRoleNavigation.UsersToRoles.Where(x=>x.IdUser== idUser));
return mapper.Map<IEnumerable<PermissionNavigationDto?>>(permissionNavigation);
}
I know that this line of code only filtering UsersToRoles entity not PermissionNavigation entity
x => x.IdRoleNavigation.UsersToRoles.Where(x=>x.IdUser== idUser)
The question is: What can be done to get all Permission Navigation related to specific user
Update1
I am looking for something like this but in ef core
SELECT PermissionNavigation.[Id]
,PermissionNavigation.[IdRole]
,Roles.Name
,Navigation.Forms
,[IdNavigation]
,UsersToRoles.IdUser
FROM [SIM].[dbo].[PermissionNavigation]
INNER JOIN Roles on Roles.Id=IdRole
INNER JOIN Navigation on Navigation.id=IdNavigation
INNER JOIN UsersToRoles on UsersToRoles.IdRole=PermissionNavigation.[IdRole]
WHERE UsersToRoles.IdUser=#IdUser
Update2
I appreciate all the help I received.
I decided to go this way:
When the user has successfully logged in, I catch the Id then I make a call to get all roles related to that user after that I make another call to get all permission navigation using role Id that I got earlier.
List<PermissionNavigationDto> navigationDtos = new();
var userRoles = await userToRoleService.GetUserRolesAsync(LoginUserDetails.Id);
foreach (var role in userRoles)
{
var navigation = await permissionNavigationService.GetPermissionNavigationByRoleIdAsync(role.IdRole);
navigationDtos.AddRange(navigation);
}
What you need is the SelectMany
//The userId concerned
int userId = 1;
//As an example, let's say you have your repository items below
List<User> users = new List<User>();
List<UsersToRole> userToRoles = new List<UsersToRole>();
List<Role> roles = new List<Role>();
//if you have to retrieve user
User myUser = users.SingleOrDefault(x => x.Id == userId);
//get all roleIds this user (userId = 1) belongs to
List<int> roleIds = userToRoles.Where(x => x.IdUser == userId).Select(us => us.IdRole).ToList();
//get the role objects, then the PermissionNavigations and flatten it out with SelectMany
List<PermissionNavigation> permissionNavigations = roles.Where(us => roleIds.Contains(us.Id)).Select(us => us.PermissionNavigations).SelectMany(x => x).ToList();
I hope this helps.

AP.NET webforms using LINQ to SQL datasource and databind with foreign keys across multiple tables

I'm attempting to build an asp.net webforms application to serve as the front end of an sql database.
see database table relations in this image
Simply getting table values is not a problem.
For example displaying values from 'Item' table like this:
var items = from i in db.Items
select new
{
ID = i.Item_ID,
Name = i.Name,
Barcode = i.BarCode,
Description = i.Description,
ItemType = i.ItemType1.ItemTypeName,
LocationCount = i.Location_Item_Juncs.Count
};
GridView1.DataSource = items;
GridView1.DataBind();
looks like this in webforms webpage
Problem is getting something like supplier information for an item.
An item can have multiple 'Supplier', 'Location' and 'ReceivedDate'!
In SQL I can query that information like this:
select Supplier.Name, Supplier.Adress, Supplier.Email, Supplier.Phone, Supplier.Supplier_Zipcode
from item, Supp_Company, Supplier
where Item_ID = 8 and Item_ID = ItemSub_ID and SupplierJunc_ID = Supplier_ID
results look like this in linqpad
These are the suppliers' information for an item with item id of 8.
Notice, there are 3 tables involved in the query (Item, Supp_Company, Supplier) and 2 pairs of values must match to select valid values.
I want to replicate that query in LINQ to use in my web forms app.
I believe the solution to this problem will apply to getting locations and 'received dates' for an item as well.
Is it possible to use a similar 'where' clause in LINQ as I can in SQL? What would the syntax look like?
sure you can, it all depends on the way you build your mappings though.
you've got a many to many scenario in here and you can
map entities relying on link table
map entities relying on link entity
link table approach (notice the modelBuilder HasMany WithMany)
void Main()
{
using (var context = new YourContext())
{
var query = from item in context.Items
from supplier in item.Suppliers
where item.ItemId == 8
select new
{
Name = supplier.Name,
Adress = supplier.Address,
Email = supplier.Email,
Phone = supplier.Phone,
Zip = supplier.Zip,
};
//...
}
}
public class YourContext : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
public YourContext() : base("MyDb")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>()
.HasMany(item => item.Suppliers)
.WithMany(supplier => supplier.Items)
.Map(m =>
{
m.MapLeftKey("ItemSub_ID");
m.MapRightKey("SupplierJunc_ID");
m.ToTable("Supp_Company");
});
}
}
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
}
public class Supplier
{
public int SupplierId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Zip { get; set; }
public ICollection<Item> Items { get; set; }
}
link entity approach (notice the modelBuilder maps every table to an entity)
void Main()
{
using (var context = new YourContext())
{
var query = from item in context.Items
join link in context.SupplierItems
on item.ItemId equals link.ItemId
join supplier in context.Suppliers
on link.SupplierId equals supplier.SupplierId
where item.ItemId == 8
select new
{
Name = supplier.Name,
Adress = supplier.Address,
Email = supplier.Email,
Phone = supplier.Phone,
Zip = supplier.Zip,
};
//...
}
}
public class YourContext : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
public DbSet<SupplierItem> SupplierItems { get; set; }
public YourContext() : base("MyDb")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>()
// ...
;
modelBuilder.Entity<Supplier>()
// ...
;
modelBuilder.Entity<SupplierItem>()
// ...
;
}
}
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
}
public class Supplier
{
public int SupplierId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Zip { get; set; }
}
public class SupplierItem
{
public int ItemId { get; set; }
public int SupplierId { get; set; }
}

How to get data out of sqlite database by using a function?

I am making a application in Xamarin forms but whatever I try I can't get the sqlite database working. I want to select all Categories where menu_ID = 1 how can I do this? I need this code inside a other page (CategoriePage.xaml.cs) can somewann help me with this?
Here is some code that I use:
(Tables.cs):
namespace AmsterdamTheMapV3
{
public class Categories
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int Menu_ID { get; set; }
public string Name { get; set; }
public Categories()
{
}
}
public class Places
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int Categorie_ID { get; set; }
public string Name { get; set; }
public Boolean Featured { get; set; }
public string OpenHours { get; set; }
public string Info { get; set; }
public string Images { get; set; }
public string Phone { get; set; }
public string Website { get; set; }
public string Adress { get; set; }
public Places()
{
}
}
public class Events
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public Events()
{
}
}
}
DB helper (TheMapDB.cs):
public class TheMapDB
{
private SQLiteConnection db;
public TheMapDB()
{
//Getting conection and Creating table
db = DependencyService.Get<ISQLite>().GetConnection();
db.CreateTable<Categories>();
db.CreateTable<Places>();
db.CreateTable<Events>();
var categories = new Categories()
{
ID = 1,
Menu_ID = 1,
Name = "test"
};
db.Insert(categories); // Insert the object in the database
}
public IEnumerable<Categories> GetCategories()
{
return (from t in db.Table<Categories>() select t).ToList();
}
//Get specific Categorie
public Categories GetCategorie(int id)
{
return db.Table<Categories>().FirstOrDefault(t => t.ID == id);
}
//Delete specific Categorie
public void DeleteCategorie(int id)
{
db.Delete<Categories>(id);
}
//Add new student to Categorie
public void AddCategorie(Categories categorie)
{
db.Insert(categorie);
}
}
}
CategoriePage.xaml.cs:
public partial class CategoriePage : ContentPage
{
static TheMapDB database;
TheMapDB categorie = new TheMapDB();
public CategoriePage(String txt)
{
InitializeComponent();
var layout = new StackLayout { Padding = new Thickness(5, 10) };
this.Content = layout;
if(txt.Equals("1"))
{
txt = "this text is number 1";
//needed code can't find soluction
}
var label = new Label { Text = txt, TextColor = Color.FromHex("#77d065"), FontSize = 20 };
layout.Children.Add(label);
}
}
thank you in advance,
I suggest you make a DAO class with a function like this: (or put this function in TheMapDB.cs)
public List<Category> GetCategoryByID(int menuID)
{
return db.Table<Category>().Where(x => x.menu_ID == menuID).ToList();
}
Then you can call this function in your DAO from everywhere you want. That seems the best solution to me.
When you put this function in the class TheMapDB.cs you can say in your CategoryPage.xaml.cs:
database.GetCategoryByID(menuID);

How to edit multiple tables in MVC using ViewModel pattern

I am trying to perform CURD operation in MVC web application in a webgrid but the problem is I have multiple tables but don't know how to perform EDIT operation by using multiple tables.
Invoice table
public Invoice()
{
this.LineItems = new HashSet<LineItem>();
}
public int Customer_ID { get; set; }
public string Customer_name { get; set; }
public string Customer_Address { get; set; }
public virtual ICollection<LineItem> LineItems { get; set; }
Product Table
public Produc()
{
this.LineItems = new HashSet<LineItem>();
}
public int Product_ID { get; set; }
public string Product_name { get; set; }
public int Unit_Price { get; set; }
public virtual ICollection<LineItem> LineItems { get; set; }
LineItems Table
public partial class LineItem
{
public int Customer_ID { get; set; }
public int LineItems_ID { get; set; }
public int Product_ID { get; set; }
public int Quantity { get; set; }
public int Total { get; set; }
public virtual Invoice Invoice { get; set; }
public virtual Produc Produc { get; set; }
}
ViewModel
public class ViewModel
{
public string Customer_name { get; set; }
public string Customer_Address { get; set; }
public int Quantity { get; set; }
public int Total { get; set; }
public string Product_name { get; set; }
public int Unit_Price { get; set; }
}
here is a class which will perform CURD operation for me
public class Class1
{
SalesOrderEntities entities = new SalesOrderEntities();
public bool SaveStudent(ViewModel viewModel)
{
try
{
var Invoice = new Invoice()
{
Customer_name = viewModel.Customer_name,
Customer_Address = viewModel.Customer_Address
};
var LineItem = new LineItem()
{
Quantity = viewModel.Quantity,
Total = viewModel.Total
};
var Produc = new Produc()
{
Product_name=viewModel.Product_name,
Unit_Price=viewModel.Unit_Price
};
return true;
}
catch
{
return false;
}
}
public bool UpdateStudent()
{
try
{
}
catch (Exception)
{
throw;
}
}
Now, here i have problem i don't know how to perform edit functionality.
Updating using Entity Framework can be fairly straight-forward as it supports change-tracking by default. Change tracking will let EF automatically manage any changes that occur to your entities once they are pulled, so that when you call SaveChanges(), these same changes will be made at the database-level.
Example Adding New Entities
Since you already have your data context, when you are creating your new entities, you'll just need to ensure that you add them to the context properly and save the changes after that is done :
// Add each of your new entities to their appropriate table in the context and then save
// your changes
entities.Invoices.Add(new Invoice(){
Customer_name = viewModel.Customer_name,
Customer_Address = viewModel.Customer_Address
});
entities.LineItems.Add(new LineItem(){
Quantity = viewModel.Quantity,
Total = viewModel.Total
});
entities.Producs.Add(new Produc(){
Product_name = viewModel.Product_name,
Unit_Price = viewModel.Unit_Price
});
// Now save your changes
entities.SaveChanges();
Example Updating Existing Entities
Updating will essentially work the same way, however you will want to have access to the identifier so that you can query the existing entity, make your changes and save them :
public ActionResult UpdateStudent(int studentId)
{
using(var entities = new SalesOrderEntities())
{
// Get your student
var student = entities.Students.FirstOrDefault(s => s.StudentID == studentId);
if(student == null)
{
// Student wasn't found
return HttpNotFound();
}
// Create a view with the existing student data
return View(student);
}
}
[HttpPost]
public bool UpdateStudent(UpdateStudentViewModel viewModel)
{
try
{
using(var entities = new SalesOrderEntities())
{
// Retrieve your existing student (or other entities)
var existingStudent = entities.Students.FirstOrDefault(s => s.StudentID == viewModel.StudentID);
// Now that you have your entity, update the appropriate properties
existingStudent.Property = viewModel.Property;
// Then finally save your changes
entities.SaveChanges();
}
}
catch(Exception ex)
{
// Something went wrong updating the user
}
}

Categories

Resources