I am using SQLite-Net PCL together with SQLite-Net extensions for the development of an application using Xamarin.
In my model I have an entity (let's call it A) which is connected to other four entities through one-to-many relationships (that are represented as lists in the model). In order to populate the tables recursively when inserting an object of A in the database I have defined the relations to use Cascade on both read, insert and delete.
In order to test if I did everything correctly I created an object of type A and populated the including lists, and finally I have inserted it into the database. The strange thing is that, for 2 of the 4 including lists the insertion went well, and all the connected objects are inserted. For other 2, instead, only the first object of the list is inserted in the database. To be clear, I am checking the database content directly with a db browser.
The following is an example of one of the objects for which only the first element of the list is inserted.
public class Username : Entity
{
public string Name
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
public Username(string username)
{
Name = username;
}
}
This is instead one of the objects for which the insertion is correct.
public class AnAddress: Entity
{
public string Address
{
get;
set;
}
public AddressType Type
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
}
To be clear, the base object Entity contains the definition of the primary key:
public abstract class Entity
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public Entity()
{
Id = -1;
}
}
And this is the way the relationships are defined:
public class A : Entity
{
public string Name
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<AnAddress> Addresses
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Username> Usernames
{
get;
set;
}
}
I then create an A object by initialising it with two lists (List and List) in the same identical way.
I finally insert the object in the database with
c.InsertWithChildren(entity, recursive: true));
Where entity is of type A and c is the connection object.
Do you have any clue about the motivation of this strange behaviour?
Related
I am making a call to an endpoint and receiving back a list of objects, let's call them Receipts. Each Receipt can contain a list of a variable amount of Vehicles. I want to be saving records with a composite key comprised of the Receipt ID and Vehicle ID.
I set up the model as:
public class Receipt
{
[Key]
[Column(Order=1)]
public string id { get; set; }
public int count { get; set; }
public List<Vehicles> vehicles { get; set; }
public class Vehicles
{
[Key]
[Column(Order=2)]
public string id { get; set; }
}
}
Next, I added Receipt to the DbContext
public class DbContext : System.Data.Entity.DbContext
{
public DbSet<Receipt> Receipt { get; set; }
}
Finally, I insert/update and save changes
try
{
using (var dbContext = new DbContext())
{
dbContext.Receipt.AddOrUpdate(receipt);
dbContext.Receipt.SaveChanges();
}
}
My program is not catching any exceptions but the table/database is completely empty. Am I structuring the object right to inform Entity Framework of how I want these rows saved? One option would be to loop through each Vehicle in a Receipt and create a new Entity that stores the Receipt ID and Vehicle ID along with other fields needed
As your database is completely empty and your code does not show any sign of it either, I'm afraid the solution to your question is to actually call
dbContext.SaveChanges();
To make it really work as expected, as mentioned in a comment, your property should be a
public virtual ICollection<Vehicles> Vehicles { get; set; }
and although I don't know if it's a problem, I would recommend putting your Vehicle class as a normal non-nested class.
I am in a strange situation right now, I have two entities:
public class Proyect
{
int id;
List<int> stages;
}
public class Stage
{
//PK, not FK to proyect
int id;
}
I know that this is not the best way to model this kind of relation (n->1)
but it was done this way and we can't change it.
Does someone know, how do I relate this entities (notation or overriding onModelCreation)?
We are using c#, ET4, VS2012, WS8.
I like to use Data Annotations for simple relationships. You must specify your key field on the Proyect table, and your foreign key on the Stage table. In the Proyect table, you should have a list of Stages, not ints, since you are relating to the Stage object. You can use the virtual keyword to use lazyloading on your related entities.
If you really need a list of type int, containing your stage Ids, just use an unmapped property.
public class Proyect{
[Key]
public int id { get; set;}
public virtual List<Stage> stages { get; set;}
[NotMapped]
public virtual List<int> stageIds {
get {
return stages == null ? null : stages.Select(t => t.id).ToList();
}
}
}
public class Stage{
public int id { get; set;}
[ForeignKey("id")]
public virtual Proyect Proyect { get; set;}
}
I am trying to use Code First to create an SQL CE 4 database. When running the sample code below, Entity Framework is inserting new records for product each time, even though the data is exactly the same. What do I need to do to make Entity Framework not create duplicate associated products? The values in the ForeignID1 and the Product object are values that already exist in the database, but Entity Framework is wiping the ID I give it and adding a new ID.
namespace MyApp.Model
{
public class MyThing
{
public int ID { get; set; }
[ForeignKey("Product")]
public int ForeignID1{ get; set; }
public virtual Product Product { get; set; }
}
}
// Data.DataManager.cs
public class DataManager : DbContext
{
public DbSet<Model.MyThing> Things{ get; set; }
public DbSet<Model.Product> Products { get; set; }
}
These are the values it has entered. There should only be one value in the table that is referenced by multiple MyThings's
In order to avoid the duplication you must attach the related entity to the context:
context.Products.Attach(myThing.Product);
context.Things.Add(myThing);
Or...
myThing.Product = null;
context.Things.Add(myThing);
...will work as well if you have set myThing.ForeignID1 to an existing Product ID.
I'm building a Windows 8 C#/XAML app that uses SQLite as a storage database, and I'm trying to create multiple tables using the SQLite-net syntax.
From what I've researched so far, a table is created based off of a class. First, I've created an "Account" class via:
public class Account
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set;}
}
And then create a table and enter in initial data later on in the code via:
private static readonly string _dbPath =
Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "data.sqlite");
using (var db = new SQLite.SQLiteConnection(_dbPath))
{
db.CreateTable<Account>();
db.RunInTransaction(() =>
db.Insert(new Account()
{
Name = "MyCheckingAccount",
Type = "Checking",
})
);
}
I want to create multiple account tables, but the db.CreateTable<Account>() syntax just creates a table and the data is inserted into the columns with db.Insert(). I don't see where to enter the name of the table itself.
How do I create multiple tables, i.e. one named "BusinessAccounts" and another "PersonalAccounts" based off of the Account class?
Is there a way to do this with SQLite-net? Or do I need to write out the SQLite command explicitly somehow?
This answer seems to be outdated, in SQLite-net you can now use an attribute on a class to ovverride the table name, for example:
[SQLite.Table("table_customers")]
public class Customer
{
[MaxLength(3)]
public string code { get; set; }
[MaxLength(255)]
public string name { get; set; }
}
So it will create/update that table.
Sqlite-Net uses the class name to create the table, as well as to update the data. To do what you want, you'll need to create separate classes. One way to get around repeating common fields is to use inheritance:
public class Account
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set;}
}
public class BusinessAccounts : Account { }
public class PersonalAccounts : Account { }
To create tables:
db.CreateTable<BusinessAccounts>();
db.CreateTable<PersonalAccounts>();
To insert data:
db.Insert(new BusinessAccounts() {...});
db.Insert(new PersonalAccounts() {...});
Just a note that the above code is untested. You'll want to make sure that the tables are created correctly (e.g. with the proper primary key and autoincrement field).
Just to add that with SQLite-net, you can change the attribute of the class by implementing an initialisation overload and setting the SQLite.TableAttribute like this:
[Table("Account")]
public class Account
{
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public Account(string name = null)
{
if (!string.IsNullOrEmpty(name))
{
TableAttribute attrname = TypeDescriptor.GetAttributes(this)(0);
attrname.Name = name;
}
}
}
The name defaults to account, but if you initialize the class with a string, it sets the attribute thus you can then create a table with that name.
DBContext class is
public class VGDB : DbContext
{
public DbSet<Planet> Planets { get; set; }
}
And model looks like:
public class Planet
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
...
public List<Building> Constructions { get; set; }
}
public class Building
{
[Key]
public int Id { get; set; }
public decimal Lvl { get; set; }
public string Type { get; set; }
}
Repository class:
public class VGDBRepository
{
private readonly VGDB _vgdb;
...
public void RemovePlanets()
{
foreach (Planet planet in _vgdb.Planets)
{
_vgdb.Planets.Remove(planet);
}
_vgdb.SaveChanges();
}
...
}
Entity Framework creates database with two tables: Planets and Buildings, related by Planet_Id field. When I call RemovePlanets() method of my VGDBRepository class it removes planets record from Planets table and sets Planet_Id field of all buildings, related with deleted planets, in Buildings table to null but not deletes them, so I have redundant records in database. I use code-first strategy to create database. How can I force Entity Framework to remove such type of related data???
You would need to cascade your deletes.
Take a look at this:
Stackoverflow Example Cascade Deletes
And this:
Msdn Code First with Enabling Cascade Deletes
I had the exact same problem and I recently figured out how to fix it so I thought I'd just add on to the answer provided by Dima.
The code that you have above for Planet and Building look very similar to how I had my related objects set up; it made sense to me to set up the relations like that. Moreover, the tables seemed to generate correctly with a FK reference back to the parent table. Like you, when I deleted my parent record (Planets, in your case), the child records (Buildings, in your case) still stuck around but the FK field had the parent ID removed so that it just had a null value. The objects were removed from the in memory collection, though, so things were getting out of sync. The thing that was really confusing to me was that Entity Framework Code First is supposed to, by default, cascade deletes like this and I didn't understand why my deletes weren't cascading.
After some digging around, I found that I had to set up a Foreign Key Association within the child class so that Entity Framework did the cascade delete correctly. So you would need to change your code to look like this:
public class Planet
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
...
public List<Building> Constructions { get; set; }
}
public class Building
{
[Key]
public int Id { get; set; }
public decimal Lvl { get; set; }
public string Type { get; set; }
//Add these two properties to create the Foreign Key Association
public int planetID { get; set; }
public Planet planet { get; set; }
}
As soon as I added the two properties and did an automigration on my database, the deletes cascaded just like I expected them to. I'm still a little unclear on why this needs to be done, but that's a subject for a separate post... I just thought that I'd share what had gotten this working for me.
Eager loading may help you. Otherwise, enable lazy loading.
foreach (Planet planet in _vgdb.Planets)
{
_vgdb.Planets.Include(p=>p.Constructions).Remove(planet);
}