How to resolve null bindings on SQLite query? - c#

I'm using the SQLite libs and SQLite.Net to query an existing database in my Windows Phone 8.1 project. So far the connection to the db is executing fine but the records being returned are null.
I took the usual debugging steps -
1.Checked the binding types and names which map to the names in my db schema.
2.Verified the data exists in the DB.
3.Stepped through the code that queries the database, found that the db bindings are null. (This suggests to me an issue with the field mappings in my POCO)
I don't get any compile time error as such but the records being returned are null.
Question:
Does anyone know why there is an issue with the bindings provided for the database mapping?
When I stepped through the SQLite.cs class I found the binding count is 0:
Query Code:
using (var dbConn = new SQLiteConnection(Path.Combine(ApplicationData.Current.LocalFolder.Path, AppDBPath), true))
{
List<ZoneInfo> zoneInfo = dbConn.Query<ZoneInfo>("select * from " + tableName).ToList<ZoneInfo>();
ObservableCollection<ZoneInfo> zoneInfoCollection = new ObservableCollection<ZoneInfo>(zoneInfo);
return zoneInfoCollection;
}
DB Mapping POCO:
public class ZoneInfo
{
//The ObjectId property is marked as the Primary Key
[SQLite.PrimaryKey]
[Column("objectId")]
public string ObjectId { get; set; }
[Column("zone")]
public string ZoneName { get; set; }
[Column("tariff_ph")]
public int? TariffPH { get; set; }
[Column("tariff_pd")]
public int? TariffPD { get; set; }
[Column("restrictions")]
public string Restrictions { get; set; }
[Column("days_of_operation")]
public string DaysOpen { get; set; }
[Column("hours_of_operation")]
public string HoursOpen { get; set; }
public ZoneInfo()
{
}
public ZoneInfo(string objectId, string zoneName, int tariffPH, int tariffPD,
string restrictions, string daysOpen, string hoursOpen )
{
ObjectId = objectId;
ZoneName = zoneName;
TariffPH = tariffPH;
TariffPD = tariffPD;
Restrictions = restrictions;
DaysOpen = daysOpen;
HoursOpen = hoursOpen;
}
}
Database schema -

Your "DB Mapping POCO" does not match your Database schema.
[Column("tariff_ph")]
public int? TariffPH { get; set; }
[Column("tariff_pd")]
public int? TariffPD { get; set; }
Should probably be
[Column("tariff_ph")]
public float TariffPH { get; set; }
[Column("tariff_pd")]
public int TariffPD { get; set; }
Since you have floating point values in your dataset and both of them are NOT NULLABLE.
I have an minimum example here Update Record in Sqlite Window Phone 8, that creates the database, inserts some data and updates the database. See if that can help you, but I'm pretty sure your data doesn't match correctly.

Related

Is calling 'Create Table' to keep database up to date a good practise?

I am new to Xamarin and SQLite so I'm asking myself what is a good practise to keep the database structure up to date, when a new column is added or so. To use the SQLite DB I am using SQLite-net-pcl.
I have read about some solutions that use a version field stored somewhere and that is used to alter the database structure manually when the version changes.
But from what I saw is that calling CreateTable on a SQLiteConnection does not only create the table, it also updates the table in the database when the underlying class changes.
So, is it a good practise just to call
SQLiteConnection db = new SQLiteConnection(dbPath);
db.CreateTable<ClassA>();
everytime the system initializes, to keep the database up to date? Every change to ClassA will then be applied to the database table without any data loss.
I test this operation, I got the following result.
First of all, My model like following code.
public class Prijem
{
[PrimaryKey, AutoIncrement, Unique]
public int BCode { get; set; }
public string Name { get; set; }
public string FirmName { get; set; }
public string ItemCode { get; set; }
public string Count { get; set; }
}
I use following code to insert data.
await App.Pdatabase.SavePrijemAsync(new Prijem() {Name="New",FirmName="55Fame" ,ItemCode="dg",Count="15"});
My SavePrijemAsync method like following code.
public Task<int> SavePrijemAsync(Prijem prijem)
{
if (IsExisted(prijem))
{
return _database.UpdateAsync(prijem);
}
else
{
return _database.InsertAsync(prijem);
}
}
I got the record like following sceenshot in the sqlite database.
Then I just add a property called MyImage
public class Prijem
{
[PrimaryKey, AutoIncrement, Unique]
public int BCode { get; set; }
public string Name { get; set; }
public string FirmName { get; set; }
public string ItemCode { get; set; }
public string Count { get; set; }
string image = " Image";
public string MyImage
{
set
{
if (image != value)
{
image = value;
}
}
get
{
return image;
}
}
}
I used update operation like following code.
await App.Pdatabase.SavePrijemAsync(new Prijem() {Name="New",FirmName="55Fame" ,ItemCode="dg",Count="15" });
And insert operation like following code.
await App.Pdatabase.SavePrijemAsync(new Prijem() { Name = "New11", FirmName = "55Fame", ItemCode = "dg", Count = "15" });
I got the result in the database like following screenshot.
In the end, We can get the result, If we add a property to the Model. this column will be added in the sqlite database, but default value we must update it manually for existing data, if we insert the new value to the database, the default value will be added.

How to correct database field mappings in SQLite?

I've attached an SQLite database to my Windows Phone 8.1 project. The connection is working but the results returned from the database are null.
Previously I posted a question on the null bindings of my query. But the answer suggested hasn't resolved the issue of a null result being returned, which is why I'm re-posting the question.
Debugging: Stepping through the SQLlite class I can see that the bindings are null although I don't see any reason for that considering the types in my DB schema and the associated field mappings in ZoneInfo.cs.
I query the database as follows which should map each field to my class ZoneInfo below.
using (var dbConn = new SQLiteConnection(Path.Combine(ApplicationData.Current.LocalFolder.Path, AppDBPath), true))
{
List<ZoneInfo> zoneInfo = dbConn.Query<ZoneInfo>("select * from " + tableName).ToList<ZoneInfo>();
ObservableCollection<ZoneInfo> zoneInfoCollection = new ObservableCollection<ZoneInfo>(zoneInfo);
return zoneInfoCollection;
}
Question:
How can I correct my database mappings so that the query result isn't empty?
**ZoneInfo.cs (mapping class for DB fields)**
public class ZoneInfo
{
//The ObjectId property is marked as the Primary Key
[SQLite.PrimaryKey]
[Column("objectId")]
public string ObjectId { get; set; }
[Column("zone")]
public string ZoneName { get; set; }
[Column("tariff_ph")]
public float TariffPH { get; set; }
[Column("tariff_pd")]
public float TariffPD { get; set; }
[Column("restrictions")]
public string Restrictions { get; set; }
[Column("days_of_operation")]
public string DaysOpen { get; set; }
[Column("hours_of_operation")]
public string HoursOpen { get; set; }
public ZoneInfo()
{
}
public ZoneInfo(string objectId, string zoneName, int tariffPH, int tariffPD,
string restrictions, string daysOpen, string hoursOpen )
{
ObjectId = objectId;
ZoneName = zoneName;
TariffPH = tariffPH;
TariffPD = tariffPD;
Restrictions = restrictions;
DaysOpen = daysOpen;
HoursOpen = hoursOpen;
}
}
Query Result: (You can see that the result of the query's field's are all null which isn't the case in the database:
The DB schema is as follows and this is a link to the DB file for testing:
The schema error causing the null mapping bindings, was the result of stale SQLLite database being referenced on the device.
Deleting the app from the phone and rebuilding the app via Visual Studio caused the database to be redeployed to the file system with the updated correct mappings. :)
1.From the Start screen, swipe left to display the Apps list. Alternatively from the Start screen, tap the right arrow (located in the lower-right).
2.Touch and hold an app.
3.Tap uninstall.
4.Tap yes.

Quick way of mapping a stored procedure in C# EF (using Database.SqlQuery)

I have the following in a using statement:
var result = await db.Database.SqlQuery<ModelLookup>("EXEC prGetModel", new {ManufacturerId = manufacturerId}).ToListAsync();
And a ModelLookup class :
public class ModelLookup
{
public int ManufacturerId { get; set; }
public int ModelId { get; set; }
public string ModelDesc { get; set; }
}
Some of the DB columns have a different name.
Is there a quick way (or data annotation) that I can use to take a column from the database and map to the desired field?
(without having to add the procedure to a DbContext)
Thanks,

Recursive Entity Update

I have an entity which holds a list of entities (same as root entity) to represent a Folder structure:
public class SopFolder
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime? LastUpdated { get; set; }
public int Status { get; set; }
public virtual ICollection<SopField> SopFields { get; set; }
public virtual ICollection<SopFolder> SopFolderChildrens { get; set; }
public virtual ICollection<SopBlock> Blocks { get; set; }
public virtual ICollection<SopReview> Reviews { get; set; }
}
This entity is stored in my DB using Code-First Approach which is working fine. I then print the entity to a KendoUI Treeview, let the user modify it and on "save" post it back to the Server to an Action as IEnumerable<TreeViewItemModel> items.
I then look for the ROOT entity with all it's children (there is only one root) and convert it back into an SopFolder object.
To get the full object updated in the database I do the following:
List<SopFolder> sopfolderlist = ConvertTree(items.First());
SopFolder sopfolder = sopfolderlist[0];
if (ModelState.IsValid)
{
SopFolder startFolder = new SopFolder { Id = sopfolder.Id };
//db.SopFolders.Attach(startFolder);
// db.SopFolders.Attach(sopfolder);
startFolder.Name = sopfolder.Name;
startFolder.LastUpdated = sopfolder.LastUpdated;
startFolder.SopFields = sopfolder.SopFields;
startFolder.SopFolderChildrens = sopfolder.SopFolderChildrens;
startFolder.Status = sopfolder.Status;
db.Entry(startFolder).State = EntityState.Modified;
db.SaveChanges();
return Content("true");
}
However this is not working. The model is not updated at all. If I shift the "entityState.Modified" before the modifications, it just creates a complete fresh duplicate of my data in the database (modified of course).
Is my approach correct or do I have to go a different path? What am I missing here? I guess there is another "hidden" id which lets the EF map the entities to the db entries but I am not sure about this. Thanks for help!
UPDATE:
Instead of creatinga new instance of SopFolder I also tried db.SopFolders.Find(sopfolder.Id) and this works for entries with no children. If I have entities with children, it creates a duplicate.
Regards,
Marcus
This is typical Disconnected Graph scenario. Please see this question for possible solutions:
Disconnected Behavior of Entity Framework when Updating Object Graph
You have already figure out the first solution - that is: update entities separately. Actually, what you should do is to fetch the original data from database and then do comparison of what have changed. There are some generic ways of doing that, some of them are described in "Programming EF DbContext" book by J.Lerman, which I strongly recommend to you before doing more coding using EF.
P.S. IMHO this is the worse downside of EF.
Replace SopFolder startFolder = new SopFolder { Id = sopfolder.Id }; with
SopFolder startFolder = db.SopFolders.FirstOrDefault(s=>s.Id.Equals(sopfolder.Id));
// then validate if startFolder != null
I recommend you to create your entity model with ParentId, not children object list. When you need treeview model collect it with recursive function from database.
public class SopFolder
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime? LastUpdated { get; set; }
public int Status { get; set; }
public virtual ICollection<SopField> SopFields { get; set; }
//public virtual ICollection<SopFolder> SopFolderChildrens { get; set; }
public int? ParentFolderId { get; set; }
public virtual ICollection<SopBlock> Blocks { get; set; }
public virtual ICollection<SopReview> Reviews { get; set; }
}
When you create children folders, select it's parent, so collect your data. In childrens case try this :
List<SopFolder> sopfolderlist = ConvertTree(items.First());
SopFolder sopfolder = sopfolderlist[0];
if (ModelState.IsValid)
{
SopFolder startFolder = new SopFolder { Id = sopfolder.Id };
//db.SopFolders.Attach(startFolder);
// db.SopFolders.Attach(sopfolder);
startFolder.Name = sopfolder.Name;
startFolder.LastUpdated = sopfolder.LastUpdated;
startFolder.SopFields = sopfolder.SopFields;
startFolder.SopFolderChildrens = sopfolder.SopFolderChildrens;
foreach (var child in sopfolder.SopFolderChildrens)
{
db.SopFolders.CurrentValues.SetValues(child);
db.SaveChanges();
}
startFolder.Status = sopfolder.Status;
db.Entry(startFolder).State = EntityState.Modified;
db.SaveChanges();
return Content("true");
}

How to get mysql char column type from C# Entity Framework Code First Model

I have just done the equivalent of Hello World by creating an Entity Framework Code First model (I have written other C# programs but not with Entity Framework). I created a model like this:
class Preferences
{
[Key]
public string StationName { get; set; }
[MaxLength(30)]
public string MainDatabase { get; set; }
[MaxLength(30)]
public string DefaultSequence { get; set; }
}
and then had a little routine to add a record
var newPrefs = new Preferences
{
StationName = "MATT",
DefaultSequence = "NONE",
MainDatabase = "NONE"
};
Preferences prefs = foo.Preferences.Add(newPrefs);
which then tries to create the database and fails when adding the primary key with the error
"BLOB/TEXT column 'StationName' used in key specification without a key length"
because it uses the data type "mediumtext" instead of CHAR or VARCHAR and MySQL can't use that type for a key.
Is there a method that is still more-or-less database agnostic that will tell MySQL to use the preferred type for the key column? Or do I just have to give up and make an integer key?
I also tried variations of the design like this but nothing worked.
class Preferences
{
[Key,DataType("CHAR"),StringLength(30)]
public string StationName { get; set; }
[MaxLength(30)]
public string MainDatabase { get; set; }
[MaxLength(30)]
public string DefaultSequence { get; set; }
}
Thanks for your help.
Try fluent mapping column type maybe:
modelBuilder.Entity<Preferences>()
.Property(p => p.Id)
.HasColumnType("CHAR(30)");
I think this is equivilent of [Column(TypeName = "CHAR(30)")] but not certain it's the same.
Edit: As per Matt's testing, length is seperate and, "char" may be case sensitive(there are a lot of settings in MySQL and other engiens related to case sensitivity in identifiers, and OS can play a part sometimes, so may vary): [Column(TypeName="char")][MaxLength(30)]
I would suggest you apply a commonly accepted practice in relational database design, which is to have meaningless primary keys. Meaningless to the business domain that is.
class Preferences
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(30)]
public string StationName { get; set; }
[MaxLength(30)]
public string MainDatabase { get; set; }
[MaxLength(30)]
public string DefaultSequence { get; set; }
}
Additional benefit: now you're free to change the StationName whenever necessary.

Categories

Resources