I'm trying to serialize a non-primitive property (struct) within an entity and Entity Framework just does not serialize it.
UPDATE: Reformulating the question and sharing the affected code.
public class ClassToPersist
{
public int Id { get; set; }
public ValueWrapper SomeValue { get; set; }
}
public struct ValueWrapper
{
public Guid Value { get; set; }
}
And in my OnModelCreating function I just declare:
modelBuilder.Entity<SomeClass>();
The result of this is not a compile, nor runtime error, Entity Framewrok just skips serializing the SomeValue field.
Please note that ValueWrapper is a struct. If I convert it to a class, it works properly by "flattening" the data inside it.
Is it that "custom" structs can't be serialized? Is it a limitation of the library or maybe there's a way to specify the serialization convention to use in this case?
Related
Im completely new in using GRPC. I have and question regarding setting up the .proto file.
In my existing solution i have forexample this class:
public class Car
{
public int Id { get; set; }
public string Brand { get; set; }
public string? Type {get; set;}
}
The Car class is placed in a Core project, since it's used by other logic around the solution. But if i like to return in my GRPC Server, is it really necessary to define it in the .proto file again ala this:
message CarReply {
int32 Id = 1;
string Brand = 2;
string Type = 3;
}
What i liked was an reference to my Car() class. Is this not possible?
If you want to use vanilla "contract first" protobuf, then yes: you need to use a schema and the generated type - it is the generated type that knows how to perform the serialization.
However! There may be an alternative; protobuf-net (and protobuf-net.Grpc) provide an independent implementation of protobuf that supports "code first" usage, which allows you to use your existing type model.
The easiest way to do this with protobuf-net is to annotate your model (using either protobuf-net's attributes, or the framework data-contract attributes - the first option giving more control); for example:
[ProtoContract]
public class Car
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Brand { get; set; }
[ProtoMember(3)]
public string? Type {get; set;}
}
It is also possible to configure everything at runtime, but that's a bit harder.
This can also be used fully with gRPC, using interfaces to define the service contract; full guidance is here
I got the following types
internal enum IssueType
{
First,
Second,
Special
}
internal class Issue
{
public int Id { get; set; }
public string Title { get; set; }
public IssueType Type { get; set; }
}
internal class SpecialIssue : Issue
{
public string Payload { get; set; }
}
Some issue types map to certain subclasses, like Special would map to SpecialIssue in this case. Others just map to Issue i.e. there are several IssueTypes without special subclasses.
Now I would like to have an Issues table to hold all these issues so I configured Issue.Type as a discriminator value.
builder.HasDiscriminator(x => x.Type)
.HasValue<Issue>(IssueType.First)
.HasValue<Issue>(IssueType.Second)
.HasValue<SpecialIssue>(IssueType.Special);
Unfortunately it seems that I cannot set multiple discriminator values for the same type so at runtime I get errors stating that some discriminators weren't mapped although I did.
What I would like to achieve is to map all subclasses to their respective discriminators and have some kind of "fallback" that maps to Issue (or just map any remaining IssueType to the Issue type manually).
Is it possible to achieve this? I couldn't figure out any way to get EFCore to do this.
I'm currently using EFCore 5.0.10.
I have the following class
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class Foo
{
public int foo { get; set; }
[ProtoIgnore]
public Bar bar { get; set; }
public int ToMD5Hash()
{
var md5 = MD5CryptoServiceProvider.Create();
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize<Foo>(ms, this);
var hash = md5.ComputeHash(ms.ToArray());
return BitConverter.ToInt32(hash, 0);
}
}
}
But I am getting an exception when calling ToMD5Hash. It says No serializer defined for type: SomeNamespace.Bar even if the property is decorated with the ProtoIgnore attribute.
Note: If I remove ImplicitFields and use ProtoMember, the serialization works.
Am I doing something wrong?
You've told it to serialize the fields. The field is not decorated with [ProtoIgnore]. The property is, and there is no good way to equate fields to properties. The use of the term "fields" here is very specific and intentional: it doesn't mean "members" - it means "fields".
Options:
tell it to serialize public members, not fields
use a manually implemented property and mark the field with [ProtoIgnore]
mark the members manually
It would be nice if automatically implemented properties allowed the same syntax as field-like events, i.e.
[field:ProtoIgnore]
public int Foo {get;set;}
However, I'm pretty sure that this is not supported in the language.
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);
}
}
}
We have been using EF CF for a while in our solution. Big fans! Up to this point, we've been using a hack to support enums (creating an extra field on the model; ignore the enum durring mapping; and map the extra field to the column in the db that we would have used). Traditionally we have been storing our enums as strings(varchars) in the DB (makes it nice and readable). Now with enum support in EF 5 (Beta 2) it looks like it only supports mapping enums to int columns in the DB....Can we get EF 5 to store our enums as their string representation.
Where "Type" is an enum of type DocumentType
public enum DocumentType
{
POInvoice,
NonPOInvoice,
Any
}
I tried to map it using:
public class WorkflowMap : EntityTypeConfiguration<Model.Workflow.Workflow>
{
public WorkflowMap()
{
ToTable("Workflow", "Workflow");
...
Property(wf => wf.Type).HasColumnType("varchar");
}
}
I thought was going to be the magic bullet but..
That just throws:
Schema specified is not valid. Errors: (571,12) : error 2019: Member
Mapping specified is not valid. The type
'Dodson.Data.DataAccess.EFRepositories.DocumentType[Nullable=False,DefaultValue=]'
of member 'Type' in type
'Dodson.Data.DataAccess.EFRepositories.Workflow' is not compatible
with
'SqlServer.varchar[Nullable=False,DefaultValue=,MaxLength=8000,Unicode=False,FixedLength=False]'
of member 'Type' in type 'CodeFirstDatabaseSchema.Workflow'.
Your thoughts?
This is currently not possible. Enum in EF has same limitations as enums in CLR - they are just named set of integer values. Check this article for confirmation:
The EF enum type definitions live in conceptual layer. Similarly to
CLR enums the EF enums have underlying type which is one of Edm.SByte,
Edm.Byte, Edm.Int16, Edm.Int32 or Edm.Int64 with Edm.Int32 being the
default underlying type if none has been specified.
I posted article and related suggestion about this problem. If you want to see this feature in the future please vote for the suggestion.
I hit this problem a few weeks ago. The best I could come up with is a bit hacky.
I have a Gender enum on the class Person, and I use data annotations to map the string to the database and ignore the enum.
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Column("Gender")]
public string GenderString
{
get { return Gender.ToString(); }
private set { Gender = value.ParseEnum<Gender>(); }
}
[NotMapped]
public Gender Gender { get; set; }
}
And the extension method to get the correct enum from the string.
public static class StringExtensions
{
public static T ParseEnum<T>(this string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
}
See this post for full details - http://nodogmablog.bryanhogan.net/2014/11/saving-enums-as-strings-with-entity-framework/