Persisting Nodatime Instant in SQL Server with ServiceStack / OrmLite - c#

I'm using NodaTime Instant for date/time storage in my DTOs with ServiceStack. I have specified the SQL type in the DTO as datetimeoffset, and ServiceStack correctly creates the table with that type. However, upon saving, I get an InvalidCastException.
Simple example:
public class ItemWithInstant
{
public int Id { get; set; }
public string Name { get; set; }
[CustomField("DateTimeOffset")
public Instant DateCreated { get; set; }
}
In the service:
public object Post(CreateItemWithInstant request)
{
var dto = request.ConvertTo<ItemWithInstant>();
Db.Save(dto); // ERROR here
return dto;
}
The specific error is an InvalidCastException with the detail of Failed to convert parameter value from a Instant to a String.
No idea why it's converting to a string when the database type is DateTimeOffset. Do I need to tell ServiceStack how to convert the value to something that works with the SQL type?
Update
Thanks to #mythz answer, I created a custom converter. I also ended up going to DATETIME2 for the SQL data type (don't think that makes much of a difference):
public class SqlServerInstantToDatetimeConverter : OrmLiteConverter
{
public override string ColumnDefinition { get { return "DATETIME2"; } }
public override DbType DbType { get { return DbType.DateTimeOffset; } }
public override object ToDbValue(Type fieldType, object value)
{
var instantValue = (Instant) value;
return instantValue.ToDateTimeUtc();
}
public override object FromDbValue(Type fieldType, object value)
{
var datetimeValue = DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
return Instant.FromDateTimeUtc(datetimeValue);
}
}
I then registered it in my AppHost.cs file:
Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(Settings.Default.LocalSqlConnectionString, SqlServerDialect.Provider));
SqlServerDialect.Provider.RegisterConverter<Instant>(new SqlServerInstantConverter());
Don't forget the FromDbType override. I forgot it initially and the field wasn't being output.
One other caveat -- since ServiceStack wants to localize all dates, I had to use the info in this answer to force all dates to a DateTimeKind.Local

[CustomField] just tells OrmLite what column definition to use when creating the table, i.e. it doesn't provide any indication on how OrmLite should treat unknown types.
You can support new fields types by registering a Custom Type Converter that's now available in the latest v4.0.44 release.

Related

Trying to map a Date to object using mysqlconnector in C#

I am trying to read information from my DAL and am wondering what built in types am I suppose to use in C# to extract the DATE Data Type in MySql to retrieve information. Am I suppose to use DateTime and convert the DateTime to Date or is there a way in C# where we can retrieve specified information when calling upon it. I have an image of the DS at the bottom
DATA_ACCESS_LAYER CODE SNIPIT
using MySqlConnector;
...
...
private IObject ReadInformation(MySqlDataReader reader, IObject object)
{
try
{
while (reader.Read)
{
((Object)object).Birthday = reader.getDateOnly("Birthday");
// ^ My Current issue of questioning what Data type
// do I have to use under the reader and do I have to change the
// IObject.Birthday datatype.
}
}
// catch code
}
Object Interface
class IObject
{
string FirstName;
...
DateOnly Birthday;
}
Object Class
class Object : IObject
{
public string FirstName { get; set; } = string.Empty;
...
public DateOnly Birthday { get; set; }
}
Image of the MySQL Database Field
class IObject
{
string FirstName;
...
DateTime Birthday; // Changed DateOnly to DateTime within the model
}
The Answer was my initial assumption that I must change the datatype from DateOnly -> DateTime. -- Answered by Flydog57

C# convert Json string property to T generic type

I have a class Type with string property Name and T property value. I am receiving a Json object with different properties, two of them are name and value.
I am creating with reflection the Type (in the example I created explicity a boolean) and I need to assign the received String value to the Type.Value property that can be any type.
How can I do that ? The type can be int string, List (any known type) or a new type that I have created. I don't want to switch over the received string type name to create a specific value type.
I want a generic way to do it to avoid updating this method every time that I create a new type in my system. If I create the Type class instance with reflection, I want also to update the value property on runtime without knowing the type.
My code doesn't handle the deserialization (this is taken place in other code that I don't have access to. Even with acess, the project where the deserialize is taken place, doesn't recognize the custom types because it is a common/util project. So in this case, I need to focus only on converting a string value to a T value.
public class Type<T>
{
public string Name { get; set; }
public Type Type => typeof(T)
public T Value { get; set; }
}
public MyResultObject
{
public string ReqId { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
var jsonResult = myResultObject //type of MyResultObject
var type = new type<bool>(); //in my code this is created with reflection
type.Name = jsonResult.Name;
type.Value = jsonResult.Value as bool ??? //I want to convert the string Value to the explicit type that can be any T type
The best approach depends on details you haven't included.
If you don't know the type at the point where you're deserializing, but you do know the type at some later point in your code, one solution could be to leave the Value property as a JToken, and then convert it when your code knows what type it needs:
public MyResultObject
{
public string ReqId { get; set; }
public string Name { get; set; }
public JToken Value { get; set; }
}
type.Value = jsonResult.Value<bool>(); // or .Value<T>()
If you need to deserialize the object to the right concrete type but you don't know at compile-time what that type might be, JSON.NET has a built-in feature to handle this: TypeNameHandling. If you serialize and deserialize your objects using serializer options like this:
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
}
... then you can make your Value property an object and JSON.NET will automatically add type metadata into the serialized object so that it can be serialized to the right type.
There are two potential down-sides to that approach, though. One is that you have to be in control of both serialization and deserialization. The other is that there may be security implications if someone you don't trust is providing the JSON: they could instantiate some random object type that you don't expect them to be able to create.
There's a middle-ground approach where you use a custom type converter to determine which type of object to create based on the value of something else on the JSON object, but you have more control over which types of objects might be created, and how that gets represented in the JSON.
Your sample doesn't demonstrate the issue well enough. If you know <T> of Type<T> just add a method on your Type<T> class that does the parsing via JsonConvert from NewtonsoftJson or JsonSerializer from System.Text.Json and problem will be solved. Am I missing something here?
public class Type<T>
{
public string Name { get; set; }
public Type GenericType => typeof(T);
public T Value { get; set; }
public void SetValueFromString(string value)
{
Value = JsonConvert.DeserializeObject<T>(value);
}
}

Mapping a custom object to multiple columns by type handler in Dapper

I'm facing a problem which has been discussed here multiple times, but unfortunately none of the answers or hints work for me.
I want to use different custom types (for example money or recordlink).
In the database I would store the different attributes of these custom types into multiple columns (example for Money: YearlyIncome.Amount, YearlyIncome.CurrencyCode).
All the hints I've found try to solve the problem using "spliton" within the query.
But I would prefer a solution using a type handler, so I don't have to manually add it to every query.
I already tried to store the information in a single database column using a kind of separator between the properties. This basically works fine using a custom type handler - but the database at the end looks "ugly".
public class Recordlink
{
public Guid Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
...
}
public class Money
{
public decimal Amount { get; set; }
public string CurrencyCode { get; set; }
...
}
public class Contact
{
Guid Id {get;set;}
Money YearlyIncome {get;set;}
Recordlink Company {get;set;}
}
I would like to achieve that when querying a list of contacts, I would just be able to access for example the property contacts[0].Company.Id (of course with a previous NULL check).
Because of so many different hints and answers to similar questions, I'm not sure if this is possible or not.
But even if this is not possible as I want to - I would prefer to know this instead of searching for ages for the solution.
Thanks and Regards
Markus
To the best of my knowledge you cannot do this in the way you are outlining here.
A standard custom typehandler looks like this:
public class MyTypeHandler : SqlMapper.TypeHandler<MyType>
{
public override MyType Parse(object value)
{
return ...;
}
public override void SetValue(System.Data.IDbDataParameter parameter, MyType value)
{
parameter.Value = ...;
}
}
So, the Parse method maps one item from the result row to your custom type. The SetValue method maps one instance of your custom type to one database parameter. Mapping multiple items of the result row to one object is not part of the possibilities.
In your shoes, instead of inventing my own property representation, I would serialize Money to JSON and save that in one column in the table. You could make a custom handler for that.
That could look something like this:
public class MoneyTypeHandler : SqlMapper.TypeHandler<Money>
{
public override Money Parse(Type destinationType, object value)
{
return JsonConvert.DeserializeObject(value.ToString(), destinationType);
}
public override void SetValue(IDbDataParameter parameter, Money value)
{
parameter.Value = (value == null) ? (object)DBNull.Value : JsonConvert.SerializeObject(value);
parameter.DbType = DbType.String;
}
}

Persisting dynamic object in DynamoDB with .NET SDK

I'm trying to persist the following class to DynamoDB using the .NET SDK:
public class MyClass
{
public string Id { get; set; }
public string Name { get; set; }
public object Settings { get; set; }
}
The problem is with the Settings property. It can be any type of object, and I do not know in advance what might be assigned to it. When I try to persist it to DynamoDB, I get the following exception:
System.InvalidOperationException: 'Type System.Object is unsupported, it has no supported members'
Both the Document Model and Object Persistence Model methods result in the same exception.
Is there a way to persist these objects in DynamoDB? Other databases like MongoDB and Azure DocumentDB will do this without any issue, and they can be deserialized to either the proper type with a discriminator, or as a dynamic JSON object.
You can use the general approach documented here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBContext.ArbitraryDataMapping.html
Here's my implementation for any arbitrary object:
public class DataConverter : IPropertyConverter
{
public object FromEntry(DynamoDBEntry entry)
{
var primitive = entry as Primitive;
if (primitive == null || !(primitive.Value is String) || string.IsNullOrEmpty((string)primitive.Value))
throw new ArgumentOutOfRangeException();
object ret = JsonConvert.DeserializeObject(primitive.Value as string);
return ret;
}
public DynamoDBEntry ToEntry(object value)
{
var jsonString = JsonConvert.SerializeObject(value);
DynamoDBEntry ret = new Primitive(jsonString);
return ret;
}
}
Then annotate your property like this:
[DynamoDBProperty(typeof(DataConverter))]
public object data { get; set; }
Little improvement to the previous answer: make converter generic so that you can deserialize to the correct type, like this:
public class SerializeConverter<T> : IPropertyConverter
{
public object FromEntry(DynamoDBEntry entry)
{
var primitive = entry as Primitive;
if (primitive is not { Value: string value } || string.IsNullOrEmpty(value))
throw new ArgumentException("Data has no value", nameof(entry));
return JsonConvert.DeserializeObject<T>(value);
}
public DynamoDBEntry ToEntry(object value) =>
new Primitive(JsonConvert.SerializeObject(value));
}
Usage:
[DynamoDBProperty(typeof(SerializeConverter<YourType>))]
public YourType data{ get; set; }
I struggled to find a good solution for interacting with thoroughly unstructured data, then eventually realized that the DynamoDBContext really isn't designed for that.
For anyone else who gets to this point, my advice is to drop to a lower abstraction level and use the AmazonDynamoDBClient directly with Dictionary<string, AttributeValue> objects.

Using ServiceStack OrmLite to create Key Value table for dynamic types

I want to create a key value table in my database along the lines of
public class KeyValue {
public string Id { get; set; }
public dynamic Value {get; set; }
}
Using a slightly modified SqlProvider I have no problems getting CreateTable<KeyValue>() to generate varchar(1024) Id, varchar(max) Value.
I have no issues saving objects to it. The problem is when I load the objects
var content = dbConn.GetById<KeyValue>("about");
content.Value at this point is a string.
Looking at the database record, the text for value does not appear to store any type information.
Is there really anything I can do better other than manually invoking ServiceStack.Text and call deserialize with the appropriate type information?
I do not need absolute dynamic, my actual use case is for polymorphism with a base class instead of dynamic. So I don't really care what type Value is whether it's the base class, dynamic, object, etc. Regardless other than using the class
public class KeyValue {
public string Id { get; set; }
public MySpecificChildType Value {get; set; }
}
I haven't been able to get anything other than a string back for Value. Can I tell OrmLite to serialize the type information to be able to correctly deserialize my objects or do I just have to do it manually?
Edit: some further information. OrmLite is using the Jsv serializer defined by ServiceStack.Text.TypeSerializer and is in no way pluggable in the BSD version. If I add a Type property to my KeyValue class with the dynamic Value I can do
var value = content.Value as string;
MySpecificChildType strongType =
TypeSerializer.DeserializeFromString(content, content.Type);
I just really want a better way to do this, I really don't like an object of 1 type going into the db coming back out with a different type (string).
I haven't worked much with the JsvSerializer but with the JsonSerializer you can achieve this (in a few different ways) and as of ServiceStack 4.0.11 you can opt to use the JsonSerializer instead, see https://github.com/ServiceStack/ServiceStack/blob/master/release-notes.md#v4011-release-notes.
Example
public abstract class BaseClass {
//Used for second example of custom type lookup
public abstract string Type { get; set; }
}
public class ChildA : BaseClass {
//Used for second example of custom type lookup
public override string Type { get; set; }
public string PropA { get; set; }
}
And then in your init/bootstrap class you can configure the serializer to emit the type information needed for proper deserialization:
public class Bootstrapper {
public void Init() {
ServiceStack.Text.JsConfig.ExcludeTypeInfo = false;
ServiceStack.Text.JsConfig.IncludeTypeInfo = true;
}
}
If you wish to use something other that the default "__type" attribute that ServiceStack uses (if you for example want to have a friendly name identifying the type rather then namespace/assembly) you can also configure your own custom type lookup as such
public class Bootstrapper {
public void Init() {
ServiceStack.Text.JsConfig.ExcludeTypeInfo = false;
ServiceStack.Text.JsConfig.IncludeTypeInfo = true;
ServiceStack.Text.JsConfig.TypeAttr = "type";
ServiceStack.Text.JsConfig.TypeFinder = type =>
{
if ("CustomTypeName".Equals(type, StringComparison.OrdinalIgnoreCase))
{
return typeof(ChildA);
}
return typeof(BaseClass);
}
}
}

Categories