SQLite-net PCL; C# Int64/long to SQLite INTEGER - visual-studio-2013

I am using Xamarin for Visual Studio 2013, and the SQLite-net PCL version 1.0.11 (https://www.nuget.org/packages/sqlite-net-pcl/).
Basically I am interested in storing a numeric value greater than 2,147,483,647 in a SQLite table where the field/column has a [PrimaryKey, AutoIncrement] decoration of a class via ORM.
The below class does create the table, and inserting values (rows) works fine:
public class Patient
{
[PrimaryKey, AutoIncrement, Column("PATIENT_NBR")]
public int PatientNumber { get; set; }
...
}
The two classes below do not create the table when using "long" or "Int64" in conjunction with PrimaryKey and AutoIncrement:
public class Patient
{
[PrimaryKey, AutoIncrement, Column("PATIENT_NBR")]
public long PatientNumber { get; set; }
...
}
or
public class Patient
{
[PrimaryKey, AutoIncrement, Column("PATIENT_NBR")]
public Int64 PatientNumber { get; set; }
...
}
In the last two examples above - when running the CreateTable:
public class CreateDatabase
{
private readonly SQLiteConnection conn;
public CreateDatabase()
{
try
{
conn = DependencyService.Get<ISQLite>().GetConnection(Constants.DatabaseName);
conn.CreateTable<Patient>();
if (conn.Table<Patient>().Count() == 0)
{
conn.Insert(new Patient { PatientCreateDate = DateTime.UtcNow, PatientFirstName = "John", PatientLastName = "Doe" });
conn.Insert(new Patient { PatientCreateDate = DateTime.UtcNow, PatientFirstName = "Jane", PatientLastName = "Doe" });
}
}
catch (Exception ex)
{
throw new Exception(string.Format("Create SQLite Connection. Error: {0}.", ex.Message));
}
}
}
I receive the exception:
ex.Message: "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY"
If I remove AutoIncrement from the decoration of the PatientNumber, it will create the table fine, but then I run into a constraint error as the value being inserted into PatientNumber always 0 (since AutoIncrement is not being used), and the field is defined as a PrimaryKey and has to be unique.
Based on the Issues opened for SQLite-net:
https://github.com/praeclarum/sqlite-net/issues/227
it makes reference to being resoved on the below:
https://github.com/praeclarum/sqlite-net/pull/345
(sorry about the two links above - it wouldn't let me post them)
In the last link above, it mentions a workaround of:
conn.ExtraTypeMappings.Add(typeof(int64), "integer")
But I am not sure of where to add this. I have tried adding this just below where "conn" is being assigned, but am getting compiler errors that:
"SQLite.SQLiteConnection does not contain a definition for "ExtraTypeMapping".
My understanding is that although SQLite can store a numeric value up to 9223372036854775807, I would be restricted by the decoration of a field with a C# class of type "Int" or "Int32", as it's maximum value is 2,147,483,647.
Am I missing something, or have I misunderstood something??
Any help would be appreciated.

Are you mixing up which PCL you want? SQLite-net vs SQLite.net vs ? There are a ton of sqlite variants out there. ;-)
You are pulling the SQLite-net PC nuget and thus are getting SQLite-net PCL 1.0.11 that was release January 22, 2015.
The patch you reference was merged on March 4, 2015:
praeclarum merged commit 1638293 into praeclarum:master on Mar 4
If that is the library that you want, pull the master branch (head or just up to that commit) and build/use that instead the the Nuget.
Or to use the SqLiteConnection.ExtraTypeMappings, maybe you looking for oysteinkrog/SQLite.Net-PCL version?:
Line 94 of SqLiteConnection.cs

Related

How can I remove a column from my SQLite table with sqlite-net-pcl that comes with Xamarin Forms?

I have a table that was created with this C# code:
public class ViewHistory
{
[PrimaryKey]
public string YYMMDD { get; set; }
public int UtcNow { get; set; }
public int Assign { get; set; }
public int Learn { get; set; }
public int Practice { get; set; }
public int Quiz { get; set; }
}
and then
db2.CreateTable<ViewHistory>();
I would like to check if the Assign column exists and then remove the Assign column if it exists. So far the suggestions I have seen have all been either quite old or have suggested creating a new table and also don't include any check to see if it exists.
Now with the latest release of sqlite-net-pcl (1.5.2) is there any way I can drop a column? If not can someone recommend a way to do this that's just using the C# features that are available to me with the PCL or with SQL that I could execute.
I saw this on SO but it doesn't really help me:
Delete column from SQLite table
SQLite does not support ALTER TABLE x DROP COLUMN x so you need to create a new table and copy data.
You can do all this via a single multi-line SQL statement and execute it, but this will walk you though the steps using the ORM as much as possible:
Note: This assumes that your model has be updated and does not include that column anymore and your current database might or might not have it...
var conn = new SQLiteConnection(.....
conn.CreateTable<ViewHistory>();
~~~
if (0 < conn.ExecuteScalar<int>("SELECT COUNT(*) AS colcount FROM pragma_table_info('ViewHistory') WHERE name='Assign'"))
{
try
{
conn.BeginTransaction();
conn.Execute("ALTER TABLE ViewHistory RENAME TO backup;");
conn.CreateTable<ViewHistory>();
// Column map and copy data
var columns = conn.GetMapping<ViewHistory>(CreateFlags.None).Columns;
var columnNames = string.Join(",", columns.Select(p => p.Name));
conn.Execute($"INSERT INTO ViewHistory ({columnNames}) SELECT {columnNames} FROM backup;");
conn.Execute("DROP TABLE backup;");
conn.Commit();
conn.Execute("VACUUM");
}
catch (Exception ex)
{
conn.Rollback();
throw ex;
}
}
Note: Typically I just use "DB Browser for SQLite", make all the table/column alterations to the database and copy the "DB Log output" that contains all the SQL statements and paste that into a single SQLConnnection.Exceute statement...

EF + ODP.NET: The specified value is not an instance of type 'Edm.Decimal'

I am using Oracle's managed ODP.NET client with Entity Framework. It's working fine. However I am having different behaviours in different servers. I am pretty sure it has something to do with DLL versions, but I could not find any differences so far.
I have these tables:
create table parent (
parent_id number primary_key,
data varchar2(100)
);
create table child (
child_id number primary key,
parent_id number references parent(parent_id)
);
And these entities:
public class Parent {
Int32 Id { get; set; }
string Data { get; set; }
}
public class Child {
Int32 Id { get; set; }
Parent Parent { get; set; }
}
This is the code:
Entities e = new Entities(connectionString);
Parent p = new Parent();
parent.Data = "TEST DATA!";
Child c = new Child();
c.Parent = p;
e.Children.AddObject(c);
e.SaveChanges(); // exception here, in one server, not on the other
I have a trigger automatically populating the id on both (parent and child), and I am using Store Generated Pattern = Identity on the entity framework configuration.
My issue is:
On my dev machine, it works perfectly as expected. Both rows are inserted on their respective tables. However, on the Server, I am getting an error saying: The specified value is not an instance of type 'Edm.Decimal'.
More info:
Oracle.ManagedDataAccess (v 4.121.1.0)
Entity Framework (v v4.0.30319.1)
On both: dev machine (working) + server (not working).
Ideas?
Trying changing the definition of your Id column from Int32 to Decimal. I've had this problem several times and I think that fixed it.
public class Parent {
Decimal Id { get; set; }
string Data { get; set; }
}
public class Child {
Decimal Id { get; set; }
Parent Parent { get; set; }
}
Installing .Net 4.5 should fix this problem as identified on this Microsoft forum. I experienced the same issue were the calls worked fine on a dev machine but failed in production and, even though my project was targeting .net 4, installing .net 4.5 solved the issue.

EF 4.3.1 Code First Updating Related Tables

Update:
This is now driving me crazy!
After much Googling etc. I am really no closer to a solution.....
However I have found one thing that is puzzling me even more - the "States" of the entities just before the m_dbContext.SaveChanges() call. (see below for full repository code)
var updateInfoState = m_dc.Entry(oldPage.UpdateInfo).State; // State is 'Modified'
var oldPageState = m_dc.Entry(oldPage).State; // State is 'Detached'
this.m_dc.SaveChanges();
Why is "oldPage" detached?
Getting quite desperate now!! ;)
Original:
I appear to be having a problem with EF Code-First updating related tables correctly.
In this simplified example, the 'UpdateInfo' table IS being updated OK with the new DateTime .... but the 'Pages' table is not being updated with the new 'Name' value.
I am seeding code-first POCOs via DropCreateDatabaseAlways / override Seed ... and EF is creating the test tables correctly - so at this point it seems to know what it is doing....
I am sure this is something simple/obvious I am missing!
All help very much appreciated!
My Class definitions:
public class Page
{
public int Id { get; set; }
public string Name { get; set; }
public virtual UpdateInfo UpdateInfo { get; set; } // virtual For Lazy loading
}
[Table("UpdateInfo")] // much better than EF's choice of UpdateInfoes!
public class UpdateInfo
{
public int Id { get; set; }
public DateTime DateUpdated { get; set; }
}
public class DomainContext : DbContext
{
public DbSet<Page> Pages { get; set; }
public DbSet<UpdateInfo> UpdateInfo { get; set; }
}
Tables created by Code-First
Pages Table
===========
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[UpdateInfo_Id] [int] NULL,
UpdateInfo Table
================
[Id] [int] IDENTITY(1,1) NOT NULL,
[DateUpdated] [datetime] NOT NULL,
My Repository code:
public Page Get(int id)
{
Page page = m_dbContext.Pages.Single(p => p.Id == id);
return page;
}
public void Update(PagePostModel model)
{
Page oldPage = Get(model.PageModel.Id); // on oldPage Name = "Hello", DateUpdated = "Last Year"
Page newPage = Mapper.Map<PageModel, Page>(model.PageModel); // on newPage Name = "Goodbye" (AutoMapper)
newPage.UpdateInfo = oldPage.UpdateInfo; // take a copy of the old UpdateInfo since its not contained in the model
newPage.UpdateInfo.DateUpdated = DateTime.UtcNow; // update to now
oldPage = newPage; // copy the updated page we grabbed from dbContext above (NB Everything looks great here..oldPage is as expected)
m_dbContext.SaveChanges(); // update - only the 'UpdateInfo' table is being updated - No change to 'Pages' table :(((
}
As you know, there is a change tracker api in Entity Framework.
To track the changes of your entities you retrieved from the database, DbContext uses its reference value.
Your "Update" function above inserts newPage into oldPage. So, DbContext never knows oldPage is a newPage. So, it is "detached".
However, for UpdateInfo, it is copy of reference in oldPage, so DbContext can track change of that. So, it is "modified".
To solve this problem, how about using the code below?
Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);
oldPage.UpdateInfo = newPage.UpdateInfo;
oldPage.UpdateInfo.DateUpdated = DateTime.UtcNow;
m_dbContext.SaveChanges();
Update
Then, use Attach & Detach methods.
Those methods help you attach and detach entities from DbContext.
Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);
// if you attach first, there will be an exception,
// because there is two entities having same id.
m_dbContext.Entry(oldPage).State = EntityState.Detached;
m_dbContext.Pages.Attach(newPage);
// if you don't set IsModified = true,
// DbContext cannot know it is changed.
m_dbContext.Entry(newPage).State = EntityState.Modified;
m_dbContext.SaveChanges();

The specified table does not exist on sql ce

I get an exception
The specified table does not exist [Limits]
while I'm trying saving new item
(App.Current as App).context.Limits.InsertOnSubmit(new Limit() { Date = DateTime.Now, Value = inputLimit });//this works
(App.Current as App).context.SubmitChanges();//here I get exception
Also I get an error on this line:
var currentLimit = (App.Current as App).context.Limits.Where(l => l.Date.Date == DateTime.Now.Date).FirstOrDefault();
Here is a "model"
public class CalCounterContext:DataContext
{
public CalCounterContext(string connstring):base(connstring)
{
}
public Table<Limit> Limits;
public Table<Meal> Meals;
}
[Table]
public class Limit
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "Int NOT NULL IDENTITY", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int Id { get; set; }
[Column]
public DateTime Date { get; set; }
[Column]
public int Value { get; set; }
}
Sometimes it works, sometimes, doesn't. What could be a reson of my problem?
This normally happens when you add a table in a later version of the database then what is being used. When you create your database context, check to see if it is up to date, and if not, update the database using the DatabaseSchemaUpdater class as described here. If it is just while you are creating the app, uninstall and re-install the app.
Also, I ran into a strange issue where intermittently I would get this error even once the app was in production without any reasoning. Often is occured when I would launch the app and then hit the home or back button to end it quickly. I ended up re-implementing the GetTable<> function used to instantiate my ITable variable in a base database class so that it would do a hard check to see if the table actually existed:
public Table<TEntity> VerifyTable<TEntity>() where TEntity : class
{
var table = GetTable<TEntity>();
try
{
// can call any function against the table to verify it exists
table.Any();
}
catch (DbException exception)
{
if (exception.Message.StartsWith("The specified table does not exist."))
{
var databaseSchemaUpdater = this.CreateDatabaseSchemaUpdater();
databaseSchemaUpdater.AddTable<TEntity>();
databaseSchemaUpdater.Execute();
}
else
{
throw;
}
}
return table;
}
I had the same intermittent error you had. Try removing the database from the device and installing the app again. I found my issue was being caused because I was making changes to the model and when I re-ran the app, I would get this error.

InsertOnSubmit with interfaces (LINQ to SQL)

In our code we have:
public interface ILogMagazine
{
string Text { get; set; }
DateTime DateAndTime { get; set; }
string DetailMessage { get; set; }
}
SimpleDataContext: DataContext
{
public Table<ILogMagazine> LogMagaines
{
get { return GetTable<ILogMagazine>(); }
}
}
We try to:
DataContext db = new SimpleDataContext("...");
ILogMagazine lg = new LogMagazine()
{
Text = "test",
DateAndTime = DateTime.Now,
DetailMessage = "test",
};
db.LogMagazines.InsertOnSubmit(lg); // Exception thrown
db.SubmitChanges();
Exception: System.InvalidOperationException: The type 'DataLayer.ILogMagazine' is not mapped as a Table..
How we can solve this problem?
The error is because you haven't applied the [Table] attribute (normally it'd go on a class type, in your case the interface type), but I don't see it working even if you did. That's how the mapping is done- when you call GetTable, it looks for the Table attribute to know where to insert/query the data from.
That said, I'm pretty sure you can't do this with an interface. The type on GetTable has to be concrete, because it uses the generic arg passed (or inferred) on GetTable to know what object to create for a query. While it might technically be able to work for inserts, the same GetTable is used for both inserts and queries- which it most certainly won't work for. Same reason XmlSerializer and DataContractSerializer don't work with interfaces.
You'll always need a concrete type, but your entity types can still implement interfaces. I'm guessing you're trying to shuttle these around somewhere (service layer, perhaps), and you'll probably need to rethink that a bit.

Resources