views:

104

answers:

2

I'm using SubSonic 3.0.0.3 and I can't seem to get the ActiveRecord .tt files to recognize and generate code for the foreign keys and relationships in my SQLite database.

I think it generates everything else just fine, but after looking at other snippets online it looks like there should be more generated code than just single classes in ActiveRecord.cs and Structs.cs for each of my tables. Looking inside Structs.cs, IsForeignKey is always false for every column, even the ones I have a foreign key defined for. Additionally, each Foreign Keys region is empty within each generated ActiveRecord class.

I'm using VS2008 with references to SubSonic 3.0.0.3, System.Data.SQLite 1.0.66.0, and System.Data.SQLite.Linq 2.0.38.0 in my project. I created the database using SQLite Expert Personal 3.1.0.2076. I made some dummy tables to try to test out how SubSonic handles one:many and many:many relationships. Here's the DDL SQLite Expert spits out for my small database:

CREATE TABLE [Person] (
[PersonID] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonName] TEXT  NOT NULL,
[PersonAge] INT  NOT NULL
);

CREATE TABLE [Group] (
[GroupID] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
[GroupName] TEXT  NOT NULL,
[GroupDescription] TEXT  NOT NULL
);

CREATE TABLE [Dog] (
  [DogID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
  [PersonID] INT NOT NULL CONSTRAINT [DogPersonFK] REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE, 
  [DogName] TEXT NOT NULL);

CREATE TABLE [GroupPersons] (
  [GroupID] INTEGER NOT NULL CONSTRAINT [GroupPersonToGroupFK] REFERENCES [Group]([GroupID]) ON DELETE CASCADE ON UPDATE CASCADE, 
  [PersonID] INTEGER NOT NULL CONSTRAINT [GroupPersonToPersonFK] REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE, 
  CONSTRAINT [sqlite_autoindex_GroupPersons_1] PRIMARY KEY ([GroupID], [PersonID]));

I know foreign keys are enabled and work in the database - SQLite Expert says they're on and when I change data in one place, like Person's PersonID, it does indeed change that PersonID in the Dog and GroupPersons tables. I've tried re-adding the database to the project, 'running custom tool' to get the .tt files to execute again, and even deleting them and adding them back. I can get a simple project to build that perform simple querying and insertions, however I tried just now to change the primary key of a single Person, Dog, or Group and x.Save() but System.Data.SQLite threw an exception for all three, saying SQLite error near "WHERE":syntax error. at the Save().

Any suggestions for what I should try to do next?

+1  A: 

I'm trying to reason this. There seems to be two issues at hand:

  • Subsonic not recognising your foreign keys
  • The x.Save() function sending that error message.

SQLite will be enforcing referential integrity on its own, so while Subsonic does not see the foreign references, SQLite does, and that's why your updates go through. Subsonic does not drive SQLite, it is driving itself, which is fine.

I'm attempting to learn what SubSonic is and does. In the mean time, I have this hypothesis: the table definitions are not parsed correctly. If x.Save() is uses automatically generated SQL, it could be that the two issues are really just one.

To validate (or invalidate) this hypothesis, could you try defining the tables thus, giving the foreign keys as table attributes, not attributes of specific fields:

CREATE TABLE [Dog] (
  [DogID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
  [PersonID] INTEGER NOT NULL,
  [DogName] TEXT NOT NULL,
  FOREIGN KEY ([PersonID]) REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE);

CREATE TABLE [GroupPersons] (
  [GroupID] INTEGER NOT NULL,
  [PersonID] INTEGER NOT NULL,
  FOREIGN KEY ([GroupID]) REFERENCES [Group]([GroupID]) ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY ([PersonID]) REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
  PRIMARY KEY ([GroupID], [PersonID]));
MPelletier
+4  A: 

It seems that the FKTables attribute for each table is not assigned in the file "SQLite.ttinclude". So i add some lines of code and managed to generate foreign key code :

After line 16 (var schema = conn.GetSchema("COLUMNS");), insert :

var schemaForeignKeys = conn.GetSchema("FOREIGNKEYS");

After line 29 (tbl.Name = row["TABLE_NAME"].ToString();), insert :

tbl.FKTables = new List<FKTable>();
var foreignKeyTables = schemaForeignKeys.Select("TABLE_NAME='" + tbl.Name + "'");
foreach (var foreignKeyTable in foreignKeyTables) {
    FKTable foreignKey = new FKTable();
    foreignKey.ThisTable = foreignKeyTable["TABLE_NAME"].ToString();
    foreignKey.ThisColumn = foreignKeyTable["FKEY_FROM_COLUMN"].ToString();
    foreignKey.OtherTable = foreignKeyTable["FKEY_TO_TABLE"].ToString();
    foreignKey.OtherColumn = foreignKeyTable["FKEY_TO_COLUMN"].ToString();
    foreignKey.OtherClass = CleanUp(foreignKey.OtherTable);
    foreignKey.OtherQueryable = foreignKey.OtherClass;
    tbl.FKTables.Add(foreignKey);
}

And after line 53 (col.IsNullable=row["IS_NULLABLE"].ToString()=="True";), insert :

col.IsForeignKey = tbl.FKTables.Any(x => x.ThisColumn == col.Name);

This is for generate the foreign key code.

Moreover, you have maybe encounter a problem when you have to delete a record which has a column to be a foreign key in an other table ? For exemple : Person(Id, Name) Dog(Id, #PersonId) If you have set the #PersonId foreign key on-delete action to "SET TO NULL", this won't work because foreign key support is disabled by default in SQLite 3.6.23.1 (version used by Data.SQLite 1.0.66.0). To enable foreign key support, you have to execute this command with each connection :

PRAGMA foreign_keys = ON;

Now, this is not supported by Data.SQLite, but it will (in version 1.0.67.0, http://sqlite-dotnet2.cvs.sourceforge.net/viewvc/sqlite-dotnet2/SQLite.NET/System.Data.SQLite/SQLiteConnection.cs?r1=1.80&amp;r2=1.81).

So you have to wait for the release or you can (like me) download the Data.SQLite source and compile the last version. It work great for me.

Good luck. And sorry for my english :)

Victor
@Victor, you're off to a great start on SO!
MPelletier