I have a problem with an SQLite database in my Xamarin.Forms PCL project.
I have followed this example from Microsoft Docs:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/databases
I've been using my own types to store data and it's worked Ok for simple custom types, but I've recently added List<int> and Attendance type to the custom object (Info).
Now when I try and create the object, i get the following errors:
Don't know about System.Collections.Generic.List`1[System.Int32]
Don't know about MyApp.Attendance
Here is the init code:
readonly SQLiteAsyncConnection database;
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<UserPrefrences>().Wait();
database.CreateTableAsync<Attendance>().Wait();
database.CreateTableAsync<Info>().Wait();
I'm using Xamarin.Forms with Xamarin.iOS.
You can not store them by default like that. However there is sqlite-net-extensions which you can use to accomplish that. You can take a look about sqlite-net-extensions here.
Using this extension you will be able to do that with TextBlob property, something like this:
public class Address
{
public string StreetName { get; set; }
public string Number { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
public class Person
{
public string Name { get; set; }
[TextBlob("PhonesBlobbed")]
public List<string> PhoneNumbers { get; set; }
[TextBlob("AddressesBlobbed")]
public List<Address> Addresses { get; set; }
public string PhonesBlobbed { get; set; } // serialized phone numbers
public string AddressesBlobbed { get; set; } // serialized addresses
}
More explanation about TextBlob from url.
Text blobbed properties Text-blobbed properties are serialized into a text property when saved and deserialized when loaded. This allows
storing simple objects in the same table in a single column.
Text-blobbed properties have a small overhead of serializing and
deserializing the objects and some limitations, but are the best way
to store simple objects like List or Dictionary of basic types or
simple relationships.
Text-blobbed properties require a declared string property where the
serialized object is stored.
I just saw that there is also similar/same questions about this topic on StackOverflow already, so you can take a look at them also.
How can you store lists of objects in SQLite.net?
Can I use a List of String in a class intended for SQLite?
Related
Problem: I'm calling a stored procedure through EF Core. When I run the stored procedure directly (via 'debug procedure'), it runs quickly, but it runs VERY slowly when called by EF's FromSqlRaw. So the problem appears to be when converting the returned data-table to a list of objects.
Setup:
Simple application with a list of blog posts. The stored procedure gets a hierarchical list of posts and associated users from a TPH table of posts, plus a table of users.
// Code is simplified, actually 8 parameters
SqlParameter depth_p = new SqlParameter("#depth", depth);
SqlParameter authorizedUserID_p = new SqlParameter("#authorizedUserID", authorizedUser.ID);
IQueryable<PostUser> query = postContext.PostUsers
.FromSqlRaw("Post.USP_ReadDebate #depth, #authorizedUserID",
parameters: new[] { depth_p, authorizedUserID_p });
List<PostUser> postUsers = query.ToList(); // This hangs.
26 columns are returned and converted by EF into the PostUser class.
PostUser holds 26 "ordinary" properties. No navigation properties, custom classes or any getters or setters that do any work.
public class PostUser
{
// Post fields
public Int32? ID { get; set; } // Primary Key
public String Text { get; set; }
public Guid OwnerID { get; set; }
public int? ParentID { get; set; } // nullable
public bool IsDisabled { get; set; }
public DateTime TimeCreated { get; set; }
public bool User_IsBanned { get; set; } = false;
// some others...
public PostUser() { }
}
Note: the stored procedure is very complex. It calls another stored procedure which fills a #spid table, then inserts the contents of that #SPID table into a table variable and returns that.
But again when debugged directly it returns quickly, so I think the problem is when EF Core is converting the returned data to the PostUser object.
Bottom Line: is there any way to get visibility into what EF Core is doing on the conversion to PostUser to find the problem?
Thank you!
I have a problem. I created the following class:
public class KnownDevice
{
public int Id { get; set; }
public string Name { get; set; }
public string IP { get; set; }
public string MAC { get; set; }
public string Type { get; set; }
public List<TriangleRegistryObject> triangles { get; set; }
public List<HexagonRegistryObject> hexagons { get; set; }
}
Now, I want to create a Database on the mobile phone itself, so I use the following code to create the table:
database = DependencyService.Get<ISQLite>().GetConnection();
database.CreateTable<KnownDevice>();
But the code crashes on the second line with the error:
System.NotSupportedException: 'Don't know about
System.Collections.Generic.List`1
Now on the internet I found that it is not allowed to add a List to a database, but I need the data in that list, so I have no idea how I can fix this problem. The list can contain arround 25 rows!
Any idea how I can solve this problem?
List<TriangleRegistryObject> is not a valid type for a SQLite database value. Your type of List<TriangleRegistryObject> does not match any of the clrType == typeof(XXXX) statements, so you get that exception. You will need to rethink the class structure a little to be able to use SQLite-net like that.
For more details about the SQLite database, you could download the source file from the link for reference.
https://learn.microsoft.com/zh-cn/samples/xamarin/xamarin-forms-samples/todo/
If you want to use ou could use List, you could use SQLite-Net Extensions instead of SQLite.
You could refer to the link. The SQLite-Net Extensions library direct to specific relationships in database.
How can you store lists of objects in SQLite.net?
I am getting all customers and including their linked operator.
The only catch is a customer can exist without an operator.
The problem I am having is when i try include the operator any customer that doesn't have a linked operator is not retrieved is there a way to still retrieve all my customers and if thy do not have an operator just have the operator object within the customer be null?
-get all customers method
public List<Customer> GetAllWithRelations()
{
return Context.Set<Customer>()
.Include(cp => cp.Operator).ToList();
}
-Cusomer object
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int? OperatorId { get; set; }
[ForeignKey("OperatorId")]
public virtual Operator Operator { get; set; }
}
-Operator Object
public class Operator
{
public int Id { get; set; }
public string Name { get; set; }
}
Although you did not specify a tag for this, by using the .Include I'm guessing it's a EntityFramework Core linq which is breaking.
I've came across the same case on EF whenever the relationship is not set to allow nulls. So, for instance, your mapping might be explicitly setting it to be required or somehow you're not setting it and EF defaults are stablishing a required map between Customer and Operator.
Just set it to optional wherever you're building your model mappings and you'll get the desired behavior.
See: https://learn.microsoft.com/en-us/ef/core/modeling/required-optional
I am facing an issue in using SQLite-Net Extensions to save data in local DB in scenario where the foreign key is referencing the same entity (self-join).
Example – Employee and Manager. Every employee has a manager and a manager is also an employee. I am facing issues in saving data in such cases. It will be really helpful if you can provide some insights. Does this extension support this kind of relationship?
Yes, relationships between objects of the same class are supported, but the foreign keys and inverse properties must be explicitly specified in the relationship property attribute because the discovery system will get it wrong as there are be two relationships with the same type.
This example is extracted from the project readme:
public class TwitterUser {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "LeaderId", "Followers",
CascadeOperations = CascadeOperation.CascadeRead)]
public List<TwitterUser> FollowingUsers { get; set; }
// ReadOnly is required because we're not specifying the followers manually, but want to obtain them from database
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "FollowerId", "FollowingUsers",
CascadeOperations = CascadeOperation.CascadeRead, ReadOnly = true)]
public List<TwitterUser> Followers { get; set; }
}
// Intermediate class, not used directly anywhere in the code, only in ManyToMany attributes and table creation
public class FollowerLeaderRelationshipTable {
public int LeaderId { get; set; }
public int FollowerId { get; set; }
}
As you can see here we have a many-to-many between Twitter users. In your case it will be a one-to-many, so you won't need the intermediate table and you'll need the foreign key (ManagerId for example) in your Person class.
I've got my EF Code First working exactly as expected aside from one small bit. I'm not sure how to name my Database File.
I'm using SQL CE, but I'm sure this applies to all forms of EF Code First.
Here's my DbContext
namespace MyApp.Domain.EntityFramework
{
public class DataContext : DbContext
{
//...
}
}
And when the database is created it's created as
MyApp.Domain.EntityFramework.DataContext.sdf
I'd prefer to just have it named
MyApp.sdf
Now I'm sure this is simple, but my Googling skills keep turning up examples where the database name is auto generated like mine.
http://www.hanselman.com/blog/SimpleCodeFirstWithEntityFramework4MagicUnicornFeatureCTP4.aspx
You need to specify a connection string (for example by creating a connection string named DataContext (your class name) in your config file, and set the desired name there.
I was looking to do the same. Managed to end up with this:
public class ShopDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Feature> Features { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Subcategory> Subcategories { get; set; }
public DbSet<Information> OrderInformation { get; set; }
public ShopDbContext() : base("Shop")
{
}
}
It will name your database "Shop" so just replace what is in the base("Shop") with whatever you want to call your database. Hope this helps.