I'm using SQLite-Net-Extensions. I'm attempting to define a OneToOne relationship so that when my Accounts model is loaded it will also include the Campaign so that I can access the campaign name.
The problem is Accounts.Campaign is always null. I have data in both tables.
Here's my tables in SQLite:
CREATE TABLE `campaigns` (
`Id` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT UNIQUE
);
and
CREATE TABLE `accounts` (
`Id` INTEGER PRIMARY KEY AUTOINCREMENT,
`CampaignId` INTEGER,
`MobileNumber` TEXT UNIQUE,
`Password` TEXT
);
Below are my models:
namespace SMA.Models
{
[SQLite.Table("accounts")]
class Accounts
{
[PrimaryKey, AutoIncrement]
public Int32 Id { get; set; }
[ForeignKey(typeof(Campaigns))]
public Int32 CampaignId { get; set; }
[MaxLength(11)]
public string MobileNumber { get; set; }
[MaxLength(50)]
public string Password { get; set; }
[OneToOne("Id")]
public Campaigns Campaign { get; set; }
}
}
and
namespace SMA.Models
{
[Table("campaigns")]
class Campaigns
{
[PrimaryKey, AutoIncrement]
public Int32 Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
}
}
I run the following code to fetch all of the accounts:
var accounts = this.db.Table<SMA.Models.Accounts>().ToList();
Also tried ...
var accounts = this.db.Query<Account>("SELECT * FROM accounts");
And ...
var accounts = this.db.Query<Account>("SELECT * FROM accounts JOIN campaigns ON accounts.CampaignID = campaigns.ID");
When I inspect accounts the account data is there, but Accounts.Campaign is null. I can't seem to see what I'm doing wrong.
Try using SQLite-Net Extension read methods instead of plain sqlite.net Query or Table. For example:
var accounts = this.db.GetAllWithChildren<Account>();
Also, make sure that you're either setting foreign keys manually or using SQLite-Net Extensions to write relationships to database.
To make SQLite-Net Extensions methods available make sure that you're importing SQLiteNetExtensions.Extensions namespace:
import SQLiteNetExtensions.Extensions;
If they're still not available, make sure that there are no duplicated sqlite.net libraries in your packages.
Related
My problem: inserting an entity with an owned property fails.
I have a Restaurant entity with an Address owned property. When I try to create an new entity and insert into the database, an exception is thrown at SaveChanges:
Cannot insert the value NULL into column 'RestaurantId', table 'AppRefDB.dbo.Addresses'; column does not allow nulls. INSERT fails.
What I did
My table Address looks like this:
CREATE TABLE [dbo].[Addresses]
(
[RestaurantId] INT NOT NULL,
[Number] NVARCHAR(8) NULL,
[Street] NVARCHAR(150) NOT NULL,
[Zip] NVARCHAR(10) NOT NULL,
[Town] NVARCHAR(50) NOT NULL,
[Site] NVARCHAR(150) NULL ,
CONSTRAINT [PK_Addresses]
PRIMARY KEY ([RestaurantId]),
CONSTRAINT [FK_Address_Restaurants_RestaurantId]
FOREIGN KEY ([RestaurantId]) REFERENCES [Restaurants] ([Id])
ON DELETE CASCADE
)
where RestaurantId is the primary key and FK from Restaurant table.
And
CREATE TABLE [dbo].[Restaurants]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
CONSTRAINT [FK_Restaurants_TCategories]
FOREIGN KEY ([IdCategory]) REFERENCES [Categories]([Id])
)
I defined my property like this in OnModelCreating:
modelBuilder.Entity<Restaurant>()
.OwnsOne(p => p.Address)
.ToTable("Addresses");
And I save like this:
await _dbContext.Set<Restaurant>()
.AddAsync(restaurant, cancellationToken);
_dbContext.SaveChangesAsync();
What I am looking for
What should I change in order to EF understand RestaurantId should get the newly created Id from Restaurant table before inserting the Address?
I am using EF Core 3.
Update works fine, I just have a problem with creating an new restaurant/address
EDIT: my model
public class Restaurant
{
[Key]
public int Id { get; set; }
[Required, StringLength(50)]
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
[Required, StringLength(150)]
public string Street { get; set; }
[StringLength(8)]
public string Number { get; set; }
[Required, StringLength(10)]
public string Zip { get; set; }
[Required, StringLength(50)]
public string Town { get; set; }
[StringLength(150)]
public string Site { get; set; }
}
Edit2 :
I tested a synchronous version as well
In this case, you have a Class object which has a collection of addresses.
using (var context = new YourContext())
{
var model= new Restaurant{ Name = "McDonald's" };
model.addresses.Add(new addresses{ street="test",.... });
model.addresses.Add(new addresses{ street="test",.... });
context.Restaurant.Add(model);
context.SaveChanges();
}
this would solve your problem.
You can add in both your classes
public ICollection<YourEntity> YourEntity{ get; set; }
or you can use foreign keys.
[ForeignKey("Restaurant")]
public long RestaurantId { get; set; }
public Restaurant Restaurant{ get; set; }
if you add this in your address entity you need to first create one resturant then add addresses separately
In fact this is probably a bug in EF 3.0
I tested with EF 3.1 (preview) and it is working fine
I am trying to use the extension in MvvmCross 4. What I am trying to do is simple: I have two tables with a one to many relationship and and want to access this.
I have two classes, BusLine and BusLineGroup. Each BusLine has one Group as foreign key. What I do in code is run a simple LINQ-Query to get all Buslines:
var testQuery =
from busLine in this._connection.Table<BusLine>()
select busLine;
The query itself works, but if I check the fields of the returned objects, the Group is always null!. See below for the class and table definitions.
What am I doing wrong? Why is the group always null? Thanks for your help.
The classes in code:
public class BusLine
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey(typeof(BusLineGroup))]
public int BusLineGroup { get; set; }
[ManyToOne]
public BusLineGroup LineGroup { get; set; }
}
public class BusLineGroup
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public string MainStations { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<BusLine> BusLines { get; set; }
}
The two tables:
CREATE TABLE "BusLineGroup" (
`Id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
`Name` TEXT NOT NULL,
`Color` TEXT NOT NULL,
`MainStations` TEXT NOT NULL
);
CREATE TABLE "BusLine" (
`Id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
`Name` TEXT NOT NULL,
`BusLineGroup` INTEGER NOT NULL,
FOREIGN KEY(`BusLineGroup`) REFERENCES `BusLineGroup`(`Id`)
);
Installed Nuget-Packages:
MvvmCross.Plugin.SQLitePCL
SQLiteNetExtensions
Note: The MvvmCross package automatically includes SQLite.Net-PCL. So both of those two use the same PCL.
You are not using any SQLite-Net Extensions method there, you are using plain sqlite.net methods that know nothing about your relationships. You have to use the WithChildren methods to read and write relationships from database.
For example, your query can be replaced with this line:
var testQuery = this._connection.GetAllWithChildren<BusLine>();
That will also fetch first-level relationships from database.
I'd recommend you to take a look at the SQLite-Net Extensions documentation and the sample project for more examples.
The project I'm in is using Entity Framework 6 with code first and has central database and a local database. The central DB is the same as the Local DB with the exception that all relationships (Foreign keys, primary keys etc) have been removed.
So lets imagine that I have the following classes:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int ProductTypeId { get; set; }
public ProductType ProductType { get; set; }
}
public class ProductType
{
public int ProductTypeId { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
if the database has a foreign key in the Product table I can do
IEnumerable<Product> products = ctx.Produts.Include(x => x.ProductType);
If the datbase doesnt have the foreign key, will EF still load the ProductType object using the code above?
Without foreign key, EF will not load the ProductType object.
You will need to do a new query. Something like this:
IEnumerable<Product> products = ctx.Produts;
foreach (var product in products )
{
ProductType type = (from t in ctx.ProductType where t.ProductTypeId == product.ProductTypeId select t).Single();
product.ProductType = type;
}
I was curious about this so I mocked up a quick test, which I ran both with and then without the foreign key in the database. The answer seems to be yes, it will work, provided you have configured your keys still in Entity Framework. Note however, of course, that the database will obviously not enforce reference constraints without the keys, although it looks like EF will.
I have a bit of a complicated one (for me anyway):
I have 2 entities in a many to many relationship.
Projects {Project_Id, ProjectName}
Users {User_Id, UserName}
Projects-Users {Id, Project_Id, User_id}
A project can be assigned to more users.
Now I want to retrieve the List of all projects (listed once) with Project_id, Name, ListOfAssignedUsers:
Id ProjectName Users
1 Project1 U1, U2, U3
2 Project2
3 Project3 U1
I can do this IN SQL, but can't figure out how to do it in LINQ!
It's LINQ to Entity - Framework(DB first):
My classes look like this:
public partial class Projects
{
public Projects()
{
this.Users = new HashSet();
}
public int Project_Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Users> Users { get; set; }
}
public partial class Users
{
public Users()
{
this.Projects = new HashSet();
}
public int User_Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Projects> Projects { get; set; }
}
(I made the change with removing the id from Projects-Users and made the combo (Project_Id, User_id) the primary key of that.
Remove the Id field from the Project-Users table. The primary key if you need one should be {Project_Id,User_id}. There should be (if not already) a foreign key constraint between Projects.Project_Id and Project-Users.Project_Id and another FK between Users.User_Id and Project-Users.User_id
If you have your model mapped in Entity Framework, then you reference it like this:
var result=context.Project.Include(p=>p.Users);
You can dump the results like this:
foreach(var record in result)
{
Console.WriteLine("{0} {1} {2}",
record.Project_Id,
record.ProjectName,
String.Join(",",record.Users.Select(u=>u.UserName)));
}
Getting the list of all the projects with the assigned users:
var projects= from p in db.Projects select new{ project_Id = p.Project_Id, projectName = p.ProjectName, userList = p.Projects-Users.Select(pu=>pu.Users.UserName).ToList()};
I wonder, if there is any way ,
to use Database-first approach with manually generated classes (models) in advance(just like Code-first approach),
but without using auto-generated code which Entity Framework creates using Database-first approach?
I have 3 Classes(first two of them Student and Courses have many to many relationship), which represents models:
First one is Student:
public class Student
{
public int StudentID { get; set;}
public string Name { get; set;}
public DateTime BirthDate { get; set;}
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Student()
{
StudentToCourses = new List<StudentToCourse>();
}
}
Then Course:
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Course()
{
StudentToCourses = new List<StudentToCourse>();
}
}
And Relation/Intermediate Class with additional properties StudentToCourse:
public class StudentToCourse
{
[Key, Column(Order = 0)]
public int StudentID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
[Key, Column(Order = 2)]
public DateTime Date { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
//public ICollection<Student> Students { get; set; }
//public ICollection<Course> Courses { get; set; }
public int Grade { get; set; }
}
Also, i created Database, using LocalDb feature in VS 2013
I have 3 Tables:
Courses:
CREATE TABLE [dbo].[Courses]
(
[CourseID] INT NOT NULL PRIMARY KEY IDENTITY,
[CourseName] NVARCHAR(100) NOT NULL,
)
Students:
CREATE TABLE [dbo].[Students]
(
[StudentID] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
[BirthDate] DATETIME NOT NULL,
)
Relation Table StudentsToCourses:
CREATE TABLE [dbo].[StudentsToCourses]
(
[StudentID] INT REFERENCES Students(StudentID) NOT NULL,
[CourseID] INT REFERENCES Courses(CourseID) NOT NULL,
[Date] DATETIME NOT NULL,
[Grade] INT NOT NULL,
PRIMARY KEY (StudentID, CourseID, Date)
)
Unfortunately, i have no luck with this approach, i do get students' data but i don't receive data from relational table and i can't receive all related grades per student.
I searched for related topics in google and in stackoverflow , but all those topics weren't helpful for me, although the example above i found in this topic.
As I suspected, the problem is not whether or not you can have a database and a class model independently. Of course you can! All these generation tools and migration stuff only serve one goal: making life easier, help you keeping both models in sync. But you can do that job yourself just as well. The end result is always: two models that – at runtime – don't interact with each other whatsoever. (Don't interact? No, not as such. There must be a middleman, an ORM, to connect both worlds.)
The reason why you don't get data is because lazy loading does not occur. Your statement is
var listOfGrades = _context.Students.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
This requires lazy loading, because the FirstOrDefault() statement executes the first part of the query. It renders a Student of which subsequently the StudentToCourses are accessed. But these don't load because the collection is not virtual. It should be
public virtual ICollection<StudentToCourse> StudentToCourses { get; set; }
This enables EF to override the collection in a dynamic proxy object that is capable of lazy loading.
But of course is is more efficient to get the collection in one statement, for example:
var listOfGrades = _context.Students.Include(s => s.StudentToCourses)
.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
Yes, you can. You just need a context with no initialization strategy (so it doesn't try to create or migrate your existing database):
public class ExistingDatabaseContext : DbContext
{
public ExistingDatabaseContext()
: base("ExistingDatabaseConnectionStringName")
{
Database.SetInitializer<ExistingDatabaseContext>(null);
}
// DbSets here for your "code-first" classes that represent existing database tables
}
Just bear in mind that this context will not be capable of doing migrations or any other form of initialization, so if you have actual true code-first tables in there as well, you'll need a separate context to manage those.