ORMLite AutoIncrement doesn't work in PostgreSQL - ormlite-servicestack

For some models in ORMLite I use Guid as Primary/Foreign Key - it allows to do bulk inserts without the need to do GetLastInsertedId() after each record inserted. I also use ShortId of int type for search purposes or passing in URL parameter (like blog post integer id) that should be autoincremented after each insert.
I don't understand how to make [AutoIncrement] work on non-primary key column in PostgreSQL.
What I'm doing wrong?
Here is an example of my class:
[Alias("ProductProperties")]
public class ProductProperty : IHasShortId, IHasName
{
public ProductProperty() { Id = Guid.NewGuid(); }
[PrimaryKey]
public Guid Id { get; set; }
[AutoIncrement]
public int ShortId { get; set; }
public string Name { get; set; }
[ForeignKey(typeof(ProductPropertyType))]
public int PropertyTypeId { get; set; }
}
It generates create table script with short_id as integer, not serial:
CREATE TABLE product_properties
(
id uuid NOT NULL,
short_id integer NOT NULL,
name text,
property_type_id integer NOT NULL,
CONSTRAINT product_properties_pkey PRIMARY KEY (id),
CONSTRAINT "FK_product_properties_product_property_types_PropertyTypeId" FOREIGN KEY (property_type_id)
REFERENCES product_property_types (short_id) MATCH Unknown
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE product_properties
OWNER TO postgres;
UPDATE:
I was able to fix this by patching ORMLite source code:
https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/src/ServiceStack.OrmLite/OrmLiteConfigExtensions.cs#L127-L128
Commented the isPrimaryKey && line.
Also edited ServiceStack.OrmLite.OrmLiteDialectProviderBase.cs lines 385-403
from
if (isPrimaryKey)
{
sql.Append(" PRIMARY KEY");
if (autoIncrement)
{
sql.Append(" ").Append(AutoIncrementDefinition);
}
}
else
{
if (isNullable)
{
sql.Append(" NULL");
}
else
{
sql.Append(" NOT NULL");
}
}
to
if (autoIncrement)
{
sql.Append(" ").Append(AutoIncrementDefinition);
}
if (isPrimaryKey)
{
sql.Append(" PRIMARY KEY");
}
else
{
if (isNullable)
{
sql.Append(" NULL");
}
else
{
sql.Append(" NOT NULL");
}
}
Why does ORMLite restricts AutoIncrement to primary keys only?

Related

Linq2db create table with DateTime2(3) fails when using in memory db and SQLiteDataProvider

I am trying to create table in memory db using Linq2Db, and SQLiteDataProvider in a netcore3.1 application.
And if mapping class has a property with attribute
[Column(DataType=DataType.DateTime2, Precision=3), Nullable ]
it gives me the following syntax error :
Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'near ")": syntax error'.
I dig for the query it generates and its this:
CREATE TABLE [testTable]
(
[Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[Created] DateTime2(3, ) NULL
)
Here is an example that I'm trying:
using System;
using LinqToDB;
using LinqToDB.Data;
using LinqToDB.Mapping;
namespace InMemoryDb
{
class Program
{
static void Main(string[] args)
{
DataConnection.AddConfiguration("default", "Data Source=Sharable;Mode=Memory;Cache=Shared",
new LinqToDB.DataProvider.SQLite.SQLiteDataProvider("SQLite.MS"));
DataConnection.DefaultConfiguration = "default";
using var db = new DataConnection("default");
db.CreateTable<TestTable>();
}
[Table(Schema="dbo", Name="testTable")]
public class TestTable
{
[Column(DataType=DataType.Int32), PrimaryKey, Identity]
public int Id { get; set; }
[Column(DataType=DataType.DateTime2, Precision=3), Nullable]
public DateTime? Created { get; set; }
}
}
}
why its generates query with DateTime2(3, ) and not a correct one DateTime2(3)?
Try this as workaround
[Column(DbType="DateTime2(3)", Nullable ]
You can use possibility of linq2db to define schema for several databases.
Note that there is no DateTime type in SQLite.
[Table(Schema="dbo", Name="testTable")]
public class TestTable
{
[Column(DataType=DataType.Int32), PrimaryKey, Identity]
public int Id { get; set; }
[Column(Configuration=ProviderName.SQLite, DataType=DataType.DateTime2), Nullable]
[Column(DataType=DataType.DateTime2, Precision=3), Nullable]
public DateTime? Created { get; set; }
}

Object value is null after deserialization (Xamarin with SQLite)

for an internship project I'm developping an app with Xamarin that will allow users to scan barcodes and create sheets of labels and purchases.
To prevent the scanned codes from being lost in case of crash etc, I've implemented SQLite to create a local backup that we could restore.
The structure is as follows : a ListOfLabelLines contains several LabelLine which each contain a Product and different other informations (such as packaging, quantity etc).
ListOfLabelLines.cs :
[Table("ListOfLabelLines")] // Indique le nom de la table qui sera générée par SQLite
public class ListOfLabelLines : BaseItem
{
private string _name { get; set; }
[TextBlob("LabelLinesBlob")]
public ObservableCollection<LabelLine> lines { get; set; }
[TextBlob("ListBlob")]
public List<String> TestList { get; set; }
public string LabelLinesBlob { get; set; } // serialized LabelLines
public string ListBlob { get; set; } // serialized TestList
public ListOfLabelLines()
{
}
public ListOfLabelLines(string name)
{
this._name = name;
lines = new ObservableCollection<LabelLine>();
TestList = new List<String>();
TestList.Add("Test1");
TestList.Add("Test2");
}
public string Name
{
get { return _name; }
set
{
_name = value;
}
}
}
}
These objects ListOfLabelLines contain an ObservableCollection<LabelLine> which I'm serializing by using the TextBlob property from SQLite-net-extensions.
However, when I retrieve the ListOfLabelLines I've stored, the ObservableCollection appears as null :
Example of null collections
Here are the methods I use to store the objects in SQlite :
public void SaveListOfLabelLines(ListOfLabelLines ShelfLabelInstance)
{
var query = from label in database.Table<ListOfLabelLines>()
where label.Name == ShelfLabelInstance.Name
select label;
var res = query.FirstOrDefault();
if (res != null)
{
database.UpdateWithChildren(ShelfLabelInstance);
Console.WriteLine("Label " + ShelfLabelInstance.Name + " updated");
}
else
{
database.InsertWithChildren(ShelfLabelInstance);
Console.WriteLine("Label " + ShelfLabelInstance.Name + " created");
}
}
and to retrieve them :
public void CheckProductsInLabelLine(string n)
{
var query = from LOLL in database.Table<ListOfLabelLines>()
where LOLL.Name == n
select LOLL;
ListOfLabelLines res = query.FirstOrDefault();
The string property linked to the TextBlob, however, contains the JSON object I need.
I thought the ObservableCollection would be obtainable when getting the object in DB since TextBlob is supposed to serialize AND deserialize.
Could anybody help ?
Thanks a lot !

Can I do an UPDATE on a JOIN query with OrmLite on ServiceStack?

I want to do an update for a specific field on a table based on the results from a query that includes a join. Using OrmLite with ServiceStack.
My Classes are as follows:
public class Document
{
public int Id { get; set; }
public string BCL_Code { get; set; }
public bool IsActive { get; set; }
public int DocumentTypeId { get; set; }
}
public class DocumentType
{
public int Id { get; set; }
public string TypeName { get; set; }
}
Trying to do the Update on the Join with the following code:
var q = db.From<Document>()
.Join<Document, DocumentType>(
(doc, type) => doc.DocumentTypeId == type.Id)
.Where(d => d.BCL_Code == "FR49")
.And<DocumentType>(t => t.TypeName == "Enrollment Process");
db.UpdateOnly(new Document { IsActive = false }, onlyFields: q);
I know I can update specific fields, and I know how to do joins, but when I try to include a join in the query, and then do an UpdateOnly, I get an error message on the db.UpdateOnly line:
The multi-part identifier "DocumentType.TypeName" could not be bound.
Is it possible to do an Update on a Join Query?
If so, what is the proper way to do it?
There's no Typed APIs for Update From Table in OrmLite yet, you can add a feature request for it.
In the meantime you can use Custom SQL, e.g:
db.ExecuteSql(#"UPDATE Document SET IsActive = #isActive
FROM Document d
INNER JOIN DocumentType t ON (d.DocumentTypeId = t.Id)
WHERE d.BCL_Code = 'FR49'
AND t.TypeName = 'Enrollment Process'",
new { isActive = false });

Xamarin Sqlite Insert does not return last primary key id

I have a simple entity:
[Table("History")]
public class History
{
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id { get; set; }
[Indexed(Name = "IDX_History", Order = 1, Unique = true)]
public int Prefix { get; set; }
[Indexed(Name = "IDX_History", Order = 2, Unique = true)]
public int Stem { get; set; }
[Indexed(Name = "IDX_History", Order = 3, Unique = true)]
public int Suffix { get; set; }
[Indexed(Name = "IDX_Favourite")]
public bool IsFavourite { get; set; }
public DateTime LastViewed { get; set; } = DateTime.Now;
}
I am trying to do an insert if its new or get the last inserted id if it already exists:
public static int SaveSolutionToHistory(Solution sol)
{
lock (_db)
{
var existingHistory = _db.Table<History>().FirstOrDefault(h => h.Prefix == sol.Prefix.Id
&& h.Stem == sol.Stem.Id
&& h.Suffix == sol.Suffix.Id);
if (existingHistory != null)
{
existingHistory.LastViewed = DateTime.Now;
_db.Update(existingHistory);
return existingHistory.Id;
}
_db.Insert(new History
{
Prefix = sol.Prefix.Id,
Stem = sol.Stem.Id,
Suffix = sol.Suffix.Id
});
return _db.ExecuteScalar<int>("SELECT last_insert_rowid()");
}
}
The top part works fine in returning an id for existing entries but for some reason the insert code always returns 1 when it should return the last inserted primary autoincrement id (as mentioned in the methods XML comment):
_db.Insert(new History
{
Prefix = sol.Prefix.Id,
Stem = sol.Stem.Id,
Suffix = sol.Suffix.Id
});
And this returns the correct id:
return _db.ExecuteScalar<int>("SELECT last_insert_rowid()");
It seems inefficient for me to do two hits like this or to do it as non strongly typed. Is there a reason why the _db.Insert is not returning the correct Id?
I am using VS 2015, with HAXM and Marshmallow x86_64 Google API Image.
I had the same problem, just found the answer here, the full xml comment is:
//
// Summary:
// /// Inserts the given object and retrieves its /// auto incremented primary key
// if it has one. ///
//
// Parameters:
// obj:
// /// The object to insert. ///
//
// Returns:
// /// The number of rows added to the table. ///
What the summary means by "retrieves" is that it updates the value of the PK field in the object which you passed to it, so if you want the last inserted id, you can use that
You can insert rows in the database using Insert. If the table
contains an auto-incremented primary key, then the value for that key
will be available to you after the insert:
public static void AddStock (SQLiteConnection db, string symbol) {
var s = new Stock { Symbol = symbol };
db.Insert (s);
Console.WriteLine ("{0} == {1}", s.Symbol, s.Id);
}
This is mean that ID will be available in object field Id.
https://components.xamarin.com/gettingstarted/sqlite-net

Cannot insert the value NULL into column error

I am attempting to save user preferences into a table but am getting a null exception and I do not understand why. This is an MVC 4 application and this is my action result where I am getting the error.
public ActionResult Go(string path, string name)
{
RaterContext r = new RaterContext();
UserData u = new UserData();
var userid = u.GetCurrentUserData().UserId;
var info = r.RatersInfo.Where(w => w.RaterName.Equals(name)).FirstOrDefault();
var pref = r.RatersPreferences.Where(w => w.RaterId.Equals(info.RaterId) && w.UserId.Equals(userid)).FirstOrDefault();
if (pref != null && pref.Count > 0)
{
pref.Count++;
r.SaveChanges();
}
else
{
pref = new RaterPreferences();
pref.UserId = userid;
pref.RaterId = info.RaterId;
pref.Count = 1;
r.RatersPreferences.Add(pref);
r.SaveChanges();
}
return Redirect(path);
}
There is nothing saved in the preferences table yet so it is hitting the else block and throwing a null exception on r.SaveChanges();. The exception is
Cannot insert the value NULL into column 'UserId', table
'WebSiteNew.dbo.RaterPreferences'; column does not allow nulls. INSERT
fails.\r\nThe statement has been terminated.
The reason this doesn't make sense is because all three properties, including the UserId have data when I step through. These are the only fields in the table. UserId = 1, RaterId = 6 and Count is clearly set to 1. They are all set as non-nullable ints and the primary key is a combination of UserId and RaterId. My Model is as follows.
public class RaterContext : DbContext
{
public RaterContext()
: base("DefaultConnection")
{
}
public DbSet<RaterInfo> RatersInfo { get; set; }
public DbSet<RaterPreferences> RatersPreferences { get; set; }
}
[Table("RaterInfo")]
public class RaterInfo
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int RaterId { get; set; }
public string RaterName { get; set; }
public string RaterLink { get; set; }
public string Section { get; set; }
public string Department { get; set; }
}
[Table("RaterPreferences")]
public class RaterPreferences
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public int RaterId { get; set; }
public int Count { get; set; }
}
Any help would be greatly appreciated as I am relatively new to MVC and ASP.NET. Let me know if you need more information. Thanks in advance!
I don't know if this helps but I tested to see what would happen on UPDATE by adding data manually so it would catch on the if block and that works. I'm only getting an error on INSERT.
Here is the create statement for the table in question.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[RaterPreferences](
[UserId] [int] NOT NULL,
[RaterId] [int] NOT NULL,
[Count] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[UserId] ASC,
[RaterId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[RaterPreferences] WITH CHECK ADD CONSTRAINT [FK_RaterPreferences_RaterInfo] FOREIGN KEY([RaterId])
REFERENCES [dbo].[RaterInfo] ([RaterId])
GO
ALTER TABLE [dbo].[RaterPreferences] CHECK CONSTRAINT [FK_RaterPreferences_RaterInfo]
GO
I have copied your code into a brand new ASP.Net MVC project with the current version of Entity Framework and I am able to run your code with no problems. I escaped the UserData acquisition with code that looks like:
RaterContext r = new RaterContext();
//UserData u = new UserData();
var userid = 1; // u.GetCurrentUserData().UserId;
var info = r.RatersInfo.Where(w => w.RaterName.Equals(name)).FirstOrDefault();
and did not have a problem running the remainder of this code.
I think you may have some problems with your keys and database structure for the RaterPreferences table. I don't know your full data-model, but I don't understand how this fits in, and it is not keyed in your code the way that you describe.
Edit:
I've modified my database tables to reflect the design you've described. You have a difference between your EntityFramework code-first implementation and your database. It looks like your database existed first, and I would remove your EntityFramework classes and rebuild them with Database First techniques.

Resources