IExtensible is not supported in structs or classes with inheritance - c#

I have this class
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=#"MarketDataEntry")]
public partial class MarketDataEntry : global::ProtoBuf.IExtensible
{
// some other properties
private float _EntryPrice;
[global::ProtoBuf.ProtoMember(270, IsRequired = true, Name=#"EntryPrice", DataFormat = global::ProtoBuf.DataFormat.FixedSize)]
public float EntryPrice
{
get { return _EntryPrice; }
set { _EntryPrice = value; }
}
}
This was working as expected. I changed the type from float to decimal for the EntryPrice property, and now I'm getting Exception message on this line
Serializer.Serialize(stream, toSerialize);
Exception message says
IExtensible is not supported in structs or classes with inheritance
Error message itself is confusing, because I made no change but float to decimal. Can that change be reason for this exception message? And what should I check for to understand where problem comes from?
I'm using protobuf-net v.2.0.0.664 on .net 4.0
EDIT
Regarding Marc's comment, MarketDataEntry is a base class for another protocontract.
[Serializable]
[ProtoContract]
public sealed class MarketData : ProtobufMarketData, IEntity
{
[ProtoMember(1)]
public long Id { get; set; }
[ProtoMember(2)]
public DateTime TransformTime { get; set; }
[ProtoMember(3)]
public DateTime CreationTime { get; set; }
[ProtoMember(4)]
public DateTime ConsumtionTime { get; set; }
[ProtoMember(5)]
public int FailedToBeConsumedCounter { get; set; }
}
EDIT 2
So what actually is being serialized is instance of MarketData, which inherits from ProtobufMarketData, which in turn contains property of type List, so I think that's where problem resist. List of MarketDataEntry objects, which contain decimal property.
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=#"ProtoBufMarketData")]
[ProtoBuf.ProtoInclude(10000, typeof(MarketData))]
public partial class ProtoBufMarketData : global::ProtoBuf.IExtensible
{
public ProtoBufMarketData() {}
private readonly global::System.Collections.Generic.List<CoreX.Shared.Entities.MarketDataDefs.MarketDataEntry> _MarketDataEntries = new global::System.Collections.Generic.List<CoreX.Shared.Entities.MarketDataDefs.MarketDataEntry>();
[global::ProtoBuf.ProtoMember(10004, Name = #"MarketDataEntries", DataFormat = global::ProtoBuf.DataFormat.Default)]
public global::System.Collections.Generic.List<CoreX.Shared.Entities.MarketDataDefs.MarketDataEntry> MarketDataEntries
{
get { return _MarketDataEntries; }
}
}

Related

How to serialize generic inheritance class types with protobuf-net

I've got a rather complex model structure and I'm hoping I can implement the serialization with protobuf-net. I've written a couple of tests and most of them work absolutely fine, but some more complex generic class structures with inheritance on both the class type and the generic type don't work.
In the example below, the models don't have constructors, but I left them out to not overflow the post with information. Please assume that the parameterless constructors are implemented in all models. Also, I'm aware that some property names don't make sense, but it's a simplified prototype for a bigger project.
note: I know that this model is probably overcomplicated, but unfortunately I'm hugely stuck with it.
Models
[ProtoContract]
public class SaveData
{
[ProtoMember(1)]
public List<Entity> entities { get; set; }
}
[ProtoContract]
public abstract class Entity
{
[ProtoMember(2)]
public Guid ID { get; set; }
[ProtoMember(3)]
public string Name { get; set; }
}
[ProtoContract]
[ProtoInclude(910, typeof(SpecificEvent<SpecificEventArguments, SpecificEventContext>))]
public class Event<T, U> : Entity
where T : EventArguments
where U : EventContext
{
[ProtoMember(22)]
public string EventName { get; set; }
[ProtoMember(23)]
public T EventArguments { get; set; }
[ProtoMember(902)]
public U EventContext { get; set; }
}
[ProtoContract]
[ProtoInclude(30, typeof(SpecificEventArguments))]
public class EventArguments
{
[ProtoMember(24)]
public int Argument { get; set; }
}
[ProtoContract]
[ProtoInclude(901, typeof(SpecificEventContext))]
public class EventContext
{
[ProtoMember(900)]
public string SomeContext { get; set; }
}
[ProtoContract]
public class SpecificEventArguments : EventArguments
{
[ProtoMember(25)]
public int SomeID { get; set; }
}
[ProtoContract]
public class SpecificEventContext : EventContext
{
}
[ProtoContract]
public class SpecificEvent<T, U> : Event<T, U>
where T : SpecificEventArguments
where U : SpecificEventContext
{
}
When I try to write some serialization logic in a simple main cmd-script, I can't seem to figure out why the serialization doesn't work.
Logic
static void Main(string[] args)
{
SaveData saveData = new SaveData();
SpecificEvent<SpecificEventArguments, SpecificEventContext> specificEvent =
new SpecificEvent<SpecificEventArguments, SpecificEventContext>(
"specific", "event",
new SpecificEventArguments(1),
new SpecificEventContext());
saveData.entities.Add(specificEvent);
using (var file = File.Create("savedata.bin"))
{
Serializer.Serialize(file, saveData);
}
SaveData loaded = null;
using (var file = File.OpenRead("savedata.bin"))
{
loaded = Serializer.Deserialize<SaveData>(file);
}
}
I get the following exception:
System.InvalidOperationException: 'Unexpected sub-type: protobuf_net_test.Program+SpecificEvent`2[[protobuf_net_test.Program+SpecificEventArguments, protobuf-net-test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[protobuf_net_test.Program+SpecificEventContext, protobuf-net-test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
Can anyone please explain me what I'm doing wrong? All help is really appreciated!!

How to configure swashbuckle correct for polymorphism

I have a problem to get the right OpenApi definition aftr update from 5.0.0 to 5.4.1
We had custom Polymorphism filter with 5.0.0 version, but they does not work correct with latest one. So I removed them and started to use GeneratePolymorphicSchemas(). It does what I need for our polymorphic models but not just for them. We have also some other abstract and concrete classes, where we don't need type discriminator. I tried different configurations but without any success. Either the generated definition is wrong or I get error on swagger UI or a server 503 error.
Link to the sample project Sample project
Here are my polimorhic models
namespace SwashbuckleTest.Models
{
public interface ITypeDiscriminator
{
string TypeDiscriminator { get; }
}
public abstract class SurveyStep : ITypeDiscriminator
{
public virtual string Id { get; set; }
public string TypeDiscriminator => GetType().Name;
}
public abstract class SurveyStepResult : ITypeDiscriminator
{
public string Id { get; set; }
public string TypeDiscriminator => GetType().Name;
}
public class BoolStep : SurveyStep
{
private string _id;
public BoolStep()
{
ResultObject = new BoolStepResult();
}
public override string Id
{
get => _id;
set
{
_id = value;
ResultObject.Id = value;
}
}
public string Question { get; set; }
public BoolStepResult ResultObject { get; }
}
public class BoolStepResult : SurveyStepResult
{
public bool Value { get; set; }
}
}
Here other models
namespace SwashbuckleTest.Models
{
public abstract class SomeBaseModel
{
public string BaseValue { get; set; }
}
public class SomeConcreteModel : SomeBaseModel
{
public int ConcreteValue { get; set; }
}
}
and configurations I have tried
options.UseAllOfToExtendReferenceSchemas();
options.GeneratePolymorphicSchemas(t =>
{
var types = t.Is<SurveyStep>() ? new List<Type>() {typeof(BoolStep)}
: t.Is<SurveyStepResult>() ? new List<Type>() {typeof(BoolStepResult)}
: null;
return types;
} , t => t.Is<ITypeDiscriminator>() ? nameof(ITypeDiscriminator.TypeDiscriminator).ToCamelCase() : null);
// or
options.GeneratePolymorphicSchemas(discriminatorSelector: t => t.Is<ITypeDiscriminator>() ? nameof(ITypeDiscriminator.TypeDiscriminator).ToCamelCase() : null);
I found the problem by my self.
The Is<> extension method does not filter abstract classes so we got here endless recursion.
It helped us to generate swagger.json, but we got other problems, that are little bit deeper.

Overriding a base virtual property with a derived type is null when passing to JsonResult

I have 2 base classes which 1 for search criteria and other 1 for search results.
I also have 2 derived classes for User object versions of both of those.
When I put a breakpoint in the controller action I can see all properties are populated as I've hardcoded.
When I call this action directly in the browser, each of my derived object properties is null.
I'm guessing the JSON serialization is not able to tell the difference from the base class to the derived one.
Is there a way to solve this?
public class BaseSearchCriteria
{
public int Page { get; set; }
public int RecordsPerPage { get; set; }
}
public class BaseSearchResults
{
public int TotalResults { get; set; }
public virtual BaseSearchCriteria SearchCriteria { get; set; }
}
public class UserSearchCriteria : BaseSearchCriteria
{
public string Username { get; set; }
}
public class UserSearchResults : BaseSearchResults
{
public new UserSearchCriteria SearchCriteria { get; set; }
}
public JsonResult Search(UserSearchCriteria model)
{
var viewModel = new UserSearchResults
{
SearchCriteria = new UserSearchCriteria
{
Page = 1,
RecordsPerPage = 15
}
};
viewModel.TotalResults = 100;
return Json(viewModel, JsonRequestBehavior.AllowGet);
}
Maybe good way to deal with it is to use generics as Daniel A. White propose.
Sample gist here.

Indexing a derived [ElasticType] only serializes the base class' properties

For the life of me, I cannot figure out why Nest is only serializing the below base class' properties when indexing an instance, even though I am telling it to index the derived class.
The base class:
[ElasticType(Name = "activity")]
public class Activity
{
[ElasticProperty(Name = "name")]
public string Name { get; set; }
[ElasticProperty(OptOut = true)]
public DateTimeOffset Timestamp
{
get { return DateTimeOffset.ParseExact(TimestampAsString, "yyyyMMddTHHmmssZ", CultureInfo.InvariantCulture).ToUniversalTime(); }
set { TimestampAsString = value.ToString("yyyyMMddTHHmmssZ"); }
}
[Obsolete("Use Timestamp, instead.")]
[ElasticProperty(Name = "timestamp")]
public string TimestampAsString { get; set; }
[ElasticProperty(Name = "application")]
public string Application { get; set; }
[ElasticProperty(Name = "application_version")]
public string ApplicationVersion { get; set; }
[ElasticProperty(OptOut = true)]
public IPAddress RemoteIpAddress
{
get { return IPAddress.Parse(RemoteIpAddressAsString); }
set { RemoteIpAddressAsString = value.ToString(); }
}
[Obsolete("Use RemoteIpAddress, instead.")]
[ElasticProperty(Name = "remote_ip_address")]
public string RemoteIpAddressAsString { get; set; }
}
The derived class:
private class SearchCountsRetrievedActivity : Activity
{
[ElasticProperty(OptOut = true)]
public PunctuationlessGuid? PrincipalIdentityId
{
set { PrincipalIdentityIdAsString = value; }
}
[ElasticProperty(Name = "principal_identity_id")]
public string PrincipalIdentityIdAsString { get; set; }
}
My index wrapper method:
public Task IndexActivityAsync<TActivity>(TActivity activity)
where TActivity : Activity
{
return _client.IndexAsync(activity);
}
No matter what I do, the serialized JSON sent over the wire only includes the Activity class' properties. What I've tried:
Making the derived classes public
Adding [ElasticType] to the derived classes
Inspecting the Nest source code (this is very difficult as the source code is very complex, and the NuGet package I'm referencing, even though it's the latest one, appears to be not forward-compatible with the latest source)
Apparently, the underlying default JSON serializer does not serialize properties with a null value. In my case, my property values were null, so that's why they weren't being serialized. The serializer works properly when values are not null.

C# accessing subclass method by casting

I have the following abstract class:
abstract class ContactQueue
{
public abstract DateTime period {
get; set; }
public abstract String type { get; set; }
public abstract String toString();
}
Now one of the sub classes of this class is the following:
class GeneralPercentageQueue : ContactQueue
{
public GeneralPercentageQueue(DateTime period)
{
this.period = period;
}
public int phone_answer_total {get; set;}
public int phone_answer_percentage_8025 { get; set; }
public int web_answer_percentage_8030 { get; set; }
public int web_answer_percentage_total { get; set; }
public int mail_answer_percentage { get; set; }
public override DateTime period { get; set; }
public override string type { get; set; }
public override string toString()
{
return period.ToString();
}
}
Now since i have several subclass of the abstract class i have created a list that can contain them all i want to loop through that list and access one of the specefic fields to do this i have attempted the following:
foreach(ContactQueue cq in p.GetGeneralEmailPercentageData(start,end))
{
foreach (ContactQueue contactqueue in finalDataList)
{
if (cq.period == contactqueue.period)
{
(GeneralPercentageQueue)contactqueue.mail_answer_percentage = (GeneralPercentageQueue)cq.mail_answer_percentage;
}
}
}
However im getting an error that there is no such field in the object ContactQueue
So how do i access it?
As others have mentioned you're missing parenthesis which is causing the error.
Instead you can use OfType(T) to filter the collections to only the type you want to compare.
foreach(GeneralPercentageQueue cq in p.GetGeneralEmailPercentageData(start,end)
.OfType<GeneralPercentageQueue>())
{
foreach (GeneralPercentageQueue contactqueue in finalDataList.OfType<GeneralPercentageQueue>())
{
if (cq.period == contactqueue.period)
{
contactqueue.mail_answer_percentage = cq.mail_answer_percentage;
}
}
}
This will prevent exceptions at runtime for mismatched types.
You need to add parentheses:
((GeneralPercentageQueue)contactqueue).mail_answer_percentage = ...;
You need to add paranthesis what is happening is the following:
contactqueue.mail_answer_percentage is calledcast is called on contactqueue.mail_answer_percentage to type GeneralPercentageQueue
Because the property mail_answer_percentage is not a property in type ContactQueue the first call fails, and you get the error that mail_answer_percentage isn't a property in ContactQueue
so your code should look like
((GeneralPercentageQueue)contactqueue).mail_answer_percentage =
((GeneralPercentageQueue)cq).mail_answer_percentage;

Categories

Resources