ServiceStack not using custom converter for NodaTime.Instant - c#

In an effort to improve performance, I recently added some denormalized SQL views to our database and created some query models that correlate. Everything is working great except for one thing -- ServiceStack Ormlite isn't using my custom type converter for some of the query model fields and I can't figure out why. What's really confusing is that it is using the converter for the model that correlates to an actual table.
I've confirmed that the field names correlate to columns being returned by the database. I've confirmed that the SQL query Ormlite is constructing includes the fields in question. I've confirmed that the data being returned from that SQL is valid. But for some reason Ormlite never hits the FromDbValue method in my converter.
Here's a simplified version of what I'm doing:
Write Model
public class Session
{
[AutoIncrement]
public int Id { get; set; }
public Instant SessionTime { get; set; } // <-- this is populated properly
// -- other fields --
[References(typeof(User))]
public int UserId { get; set; }
[Reference]
public User User { get; set; }
}
Query Model
public class SessionQueryModel
{
public int Id { get; set; }
public Instant SessionTime { get; set; } // <-- this IS NOT populated
// -- other fields --
public int UserId { get; set; }
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
}
The write model correlates to a table named Session. The query model correlates to a view called SessionQueryModel that already has the User table joined in and retrieves the name fields.
My Instant data is stored in a DATETIME2 field, and I register my custom converter properly. If anything were wrong there, the write model wouldn't successfully be hydrated.
I can't for the life of me figure out what's going on. I can't see any difference between the two, the field names match up, etc. The only thing I can figure is that Ormlite for some reason can't glean the type from a view in the same way it can from a table. Any ideas what might be causing this?
UPDATE
It appears that it's not just my Instant fields. There are a handful of other fields that aren't being populated as well, even though they're in the data with names matching the properties in the query model POCO. There doesn't seem to be any rhyme or reason. Some of these are simple VARCHAR columns mapping to string properties.
Now I'm really confused.

I figured it out. The class in question had two enum properties. One was of type SessionStates and the other SessionStatuses. I guess I didn't pay attention to the Intellisense entry originally, and the similar names made it not so easy to see.
Anyway, the enum values are stored in SQL as strings, not integers, and when ServiceStack was mapping, I can only assume the inability to parse the string to an enum value caused all mapping to cease.
Demis, if you're out there, this looks like it's probably a bug. I would think the system should throw an error if there was a parsing error as opposed to simply stopping mapping.

Related

ServiceStack OrmLite: Use default database constraint instead of null value from data model

I'm still pretty new to these technologies. I've run into a small issue, and it's one that can be fixed by writing some lazy code...but OrmLite and ServiceStack streamline so many things, I'm wondering if there's a better way to do this.
So, I have a data model:
public class cctv_camera
{
[AutoIncrement]
public int I_id { get; set; }
public string I_sid { get; set; }
public string C_store_id { get; set; }
// .... others
}
This data model is mapped to a table, cctv_camera. There's another model (call it CamDetail) being sent to the client after some joins from this table. We are receiving back a CamDetail object from the client on a POST to save to the database and populating an instance of lp_cctv_camera with the data (new lp_cctv_camera().PopulateWith(CamDetail);).
Here's the thing: the I_sid column is a NOT NULL column with a default constraint that generates a hash for that row. It's something that the database is responsible for, so new items should not INSERT this column; it should be generated by the constraint.
Is there any way to db.Insert(lp_cctv_camera) while ignoring this column? I have tried the [Ignore] attribute on the definition, but we still need it in the definition to send existing I_sids out to the client. I really can't find anything in the docs. Any help is appreciated!
We've added an explicit [IgnoreOnInsert] attribute you can use to ignore specific properties on Insert which is available on v4.5.13 on MyGet.
Prior to v4.5.13 you can use the [Compute] attribute to get the similar behavior and ignore fields during inserts, e.g:
public class cctv_camera
{
[AutoIncrement]
public int I_id { get; set; }
[Compute]
public string I_sid { get; set; }
public string C_store_id { get; set; }
// .... others
}

How, using code-only, do I map Vertical Inheritance using multiple, existing "type" values? (Telerik OpenAccess)

I am trying to map a vertical inheritance between a base class and derived class (obviously). I am using code-only and the FluentAPI approach for which I have found almost ZERO documentation. I have found a couple of docs on vertical inheritance and code-only but very few on managing the discriminator column/value.
S​o ​I have been trying to extrapolate how to do it from a combination of this blog post and some documentation on implementing vertical inheritance using code-only. All to no avail.
You will see that I have a "Deliverables" base table and "PrintDeliverables" derives from that. There will be other derivatives coming down the road. But I figured I would start with one first.
Anyway, I naturally have models that map to the tables.
public class PrintDeliverable : BDeliverableBase
{
public String PaperItemNumber { get; set; }
public String PrinterModel { get; set; }
public Boolean? ColorOption { get; set; }
public String ProductCode { get; set; }
}
public class BDeliverableBase : BModelBase, IDeliverable, ISingleID
{
public Int64 ID { get; set; }
public String Label { get; set; }
public String Description { get; set; }
public IList<DeliverableAttribute> Attributes { get; set; }
public Int64 TypeID { get; set; }
public DeliverableType Type { get; set; }
}
public class DeliverableType : BModelBase, ISingleID
{
public Int64 ID { get; set; }
public String Label { get; set; }
public String Description { get; set; }
public IList<BDeliverableBase> Deliverables { get; set; }
}
I have standard mapping which maps the fields and types, sizes, etc. When I run it with no further additions I get the Error ...
Invalid Column name voa_class
My research uncovered that the ORM is attempting to update a "discriminator" column with a value that will tie the base table and table with the derived data together. I learned that I can change the name of the column it uses, which I did in the BASE CLASS mapping (BDeliverableBase). I changed it to use the "DeliverableTypeId" column since the DeliverableType indicates which TYPE of deliverable it is. Since each TYPE will have it's own derivative table this would be an appropriate value to associate which derivative table to use.
MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
map.HasDiscriminator().ToColumn("DeliverableTypeId");
It appears to like this better but it wants to insert this crazy number (ex// 612274703-854) into the DeliverableTypeId column which, of course, being a foreign key to the DeliverableTypes table is not allowed.
Insert of '612274703-' failed: Telerik.OpenAccess.RT.sql.SQLException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_DeliverableType". The conflict occurred in database "DB1", table "dbo.DeliverableTypes", column 'DeliverableTypeId'
I learned that OpenAccess/DataAccess generates a hash value to insert into the discriminator column. I do not want this, in fact I know that the value must be one of the IDs available in the DeliverableType. So I read in one of the docs that you could define what value to assign to the discriminator. The example applied a hard-coded value to the base class (dog and cat derived from animal) ...
animal.HasDiscriminatorValue("23");
This presented one problem ... I do not have a single value I can hard-code. It could one of MANY values present in the DeliverableTypes table. However, for the sake of proving out the concept I hard-coded the value of an existing record
MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
map.HasDiscriminator().ToColumn("DeliverableTypeId");
map.HasDiscriminatorValue("819");
I continued to get the identical error from before. So it doesn't appear that it was applying my hard-coded value. So ... I thought, while hard-coding the value is a little hacky it would make more sense to define that in the mapping for the derived class. That would resolve my hard-coded issue since ALL instances of that derived class WOULD indeed be of the same DeliverableTypeId. So I tried ...
MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
map.HasDiscriminator().ToColumn("DeliverableTypeId");
MappingConfiguration<PrintDeliverable> map = new MappingConfiguration<PrintDeliverable>();
map.HasDiscriminatorValue("819");
This resulted in the Error
Insert of '612274703-857' failed: Telerik.OpenAccess.RT.sql.SQLException: String or binary data would be truncated.
So I got a different error but still the same poblem. This type it was trying to stuff the ORM generated discriminator value (instead of my 819) into what I am assuming is my defined discriminator column (DeliverableTypeId), although the different error makes me suspicious that it was targeting a different column.(?)
In an effort to not drag this out too long I have tried several combinations of where to these "HasDiscriminator" and "HasDiscriminatorValue" assignments go but always end up with one or the other of these errors. So the question is ...
How, using code-only, do I map Vertical Inheritance using multiple, existing "type" values?

How Do I Query Objects With Nested Properties Using NHibernate?

Okay, I've seen some similar questions to this, but the answers either confused me or seemed completely over-engineered, so I'd like to ask my own question.
I have a class called Tree, which has an object property from the class Plot, which has an object property from the class Year, which has an object property from the class Series, which has a string property called Id. This is summarized below.
public class Tree {
public virtual Plot Plot { get; set; }
// other properties...
}
public class Plot {
public virtual Year Year { get; set; }
// other properties...
}
public class Year {
public virtual Series Series { get; set; }
// other properties...
}
public class Series {
public virtual string Id { get; set; }
// other properties...
}
Each of these classes corresponds to the table of a database, and properties correspond to foreign key fields (for example, the Trees table has a field called PlotKey, which refers to a record in the Plots table). All I want to do is load all trees from the database whose corresponding Series have the Id "Adrian_2012" or "IPED Sample". I thought this would be a pretty easy taking using the following code:
IList<Tree> trees = session.CreateCriteria<Tree>()
.Add(Expression.Or(
Expression.Eq("Plot.Year.Series.Id", "Adrian_2012")
Expression.Eq("Plot.Year.Series.Id", "IPED Sample")
))
.List<Tree>();
But this is throwing: "NHibernate.Exceptions.GenericADOException : could not execute query". I have tried using Expression.Disjunction, I have tried using Aliases, Restrictions, and SimpleExpressions, and I know that nothing stupid like unmapped properties or misspelled criteria is occurring. The only other thing I've seen that might help is the ISession.QueryOver<>() function, but I get very confused by lambda expressions. Does anyone have a solution for me that would use just a simple CreateCriteria<> statement like that above?
Thanks in advance!
One not nice side of the Criteria queries is, that we have to define associations chain explicitly. I.e. we have to introduce JOIN:
15.4. Associations (cite:)
You may easily specify constraints upon related entities by navigating associations using CreateCriteria().
So to have a JOIN we need syntax like this
var trees = session
.CreateCriteria<Tree>()
.CreateCriteria("Plot", "p")
.CreateCriteria("Year", "y")
.CreateCriteria("Series", "s")
.Add(Expression.Or(
Expression.Eq("s.Id", "Adrian_2012")
Expression.Eq("s.Id", "IPED Sample")
))
.List<Tree>();
Also, check this:
NHibernate - CreateCriteria vs CreateAlias

SQLite Exception when creating table using OData object containing reference to itself

The following code:
oc.CreateTablesAsync(typeof(ODataService.UserInformationListItem), typeof(ODataService.CategoriesItem), typeof(ODataService.PostsItem));
Gives me this exception:
System.NotSupportedException: Don't know about ODataApp.ODataService.UserInformationListItem
At first I was just creating a table for PostsItems, which is the object i need to store, but then I got the above exception but then for ODataApp.ODataService.CategoriesItem.
Since PostItem has the property Category, which is a List<CategoriesItem> I figured I had to add CategoriesItem as a table as well.
After doing so I got an error for the property CreatedBy on PostsItems, which is a UserInformationListItem, now this is when it gets tricky.
I also added UserInformationListItem to the CreateTables function which gave me my current problem.
The object UserInformationListItem also contains a CreatedBy property which is also an UserInformationListItem.
I'm looking for a way around this without having to alter or recreate local versions of these objects.
These objects are defined on Sharepoint which I cannot edit, and are obtained by the OData service reference. If anymore information is needed to supply me with an answer, please let me know
Based on your sample code I presume you're using sqlite-net.
Sqlite-net is not a real ORM - it only has support for basic data types and can't handle your custom types as properties. You can check it your self in SqlType() static method of Orm class (SQLite.cs).
It's not self referencing that's causing problems, it's any references between the classes. Creating tables for the following two classes will fail as well with NotSupportedException:
public class CategoryItem
{
public int Id { get; set; }
public string Title { get; set; }
}
public class PostItem
{
public int Id { get; set; }
public string Label { get; set; }
public List<CategoryItem> Categories { get; set; }
}
You'll need to create your own "database" classes which will map to database tables one-to-one. To handle UserInformationListItem you'll just add it's key value as a property to PostItem. If PostItem and CategoryItem have a many to many relationship you'll need to add a cross-reference table as well.

Setting what table a DbContext maps to

In an application I'm working on, I have what are essentially a bunch of lookup tables in a database which all contain two things: The ID (int) and a Value (string).
There's only a handful of them, but I want to map all of them to a single Context which depends on the table name. Something like:
class LookupContext : DbContext
{
public DbSet<Lookup> Lookups { get; set; }
public LookupContext(String table)
{
// Pseudo code:
// Bind Lookups based on what table is
Lookups = MyDatabase.BindTo(table);
}
}
So if I create a new LookupContext("foo"), it binds against the foo table. If I do new LookupContext("bar") it uses the bar table, and so forth.
Is there any way to do this? Or do I have to create a separate context + model for every table I have?
This is more or less my first time doing this, so I'm not really sure if what I'm doing is right.
The answer we should be able to give you is to use enums, but that's not available quite yet - it's in the next version of EF. See here for details: http://blogs.msdn.com/b/adonet/archive/2011/06/30/walkthrough-enums-june-ctp.aspx
With earlier versions of EF, you can simply create a class per lookup value (assuming state as an example) and have code that looks something like the following:
public class State
{
public int StateId {get;set;}
public string StateName {get;set;}
}
public class LookupContext : DbContext
{
public DbSet<State> States {get;set;}
// ... more lookups as DbSets
}
This will allow you to use one context but will still require one class per table. You can also use the fluent API if you want your table/column names to differ from your class/property names respectively. Hope that helps!
I actually realized I was completely over complicating things beyond reason. There was no reason for storing multiple tables with two columns.
I'm better off storing my data as:
public class LookupValue
{
public string LookupValueId { get; set; }
public string Value { get; set; }
public string LookupType { get; set; }
}
Where the third field was simply the name of the table that I was previously storing in the database.
I'm still interested in the idea of mapping a single Context class to multiple tables, but I believe what I described above is the least convoluted way of accomplishing what I need.

Categories

Resources