I have the problem described here:
http://social.msdn.microsoft.com/Forums/en-AU/csharplanguage/thread/b310c71a-2479-4a93-888a-29294cecbe09
They give a solution using a SerializationBinder. Is there another alternative?? Like decorating my classes with a different namespace and assembly?? The reason is that I have some classes with this problem used many times, and I have to add the line "formatter.Binder = ..." in each part of the code. It would be easier to apply my hipothetic second solution.
Thanks.
If the assembly version changes, serialized objects become invalid. I once made changes to the source code of Protobuf-Net to avoid the version check, and it was fairly easy to do so. However, it can lead to unexpected results (data ending up in the wrong fields), unless you avoid the implicit fields, and set an index to each field manually using annotations. That's the advantage or Protobuf-Net, that you have control over the order of the fields in the serialized stream.
Another solution is to use custom serialization? Something like:
[Serializable]
public class MyObject : ISerializable
{
public int n1;
public int n2;
public String str;
public MyObject()
{
}
protected MyObject(SerializationInfo info, StreamingContext context)
{
n1 = info.GetInt32("i");
n2 = info.GetInt32("j");
str = info.GetString("k");
}
[SecurityPermissionAttribute(SecurityAction.Demand,
SerializationFormatter =true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("i", n1);
info.AddValue("j", n2);
info.AddValue("k", str);
}
}
Related
I have an ASP .Net Core 2.2. Web API. I'd like to speed up performance by using MemoryCache. However, I need to cache 2 different types, both which use integer keys. The one type is a list of users and the other is a list of groups.
Now, I'm adding the MemoryCache service in the Startup.cs file:
services.AddMemoryCache();
and then I'm using dependency injection to access this cache in two different places (in Middleware and in a service I wrote).
From what I understand, both these caches are the same instance. So when I add my various users and groups to it, since they both have integer keys, there will be conflicts. How can I handle this? I thought about using two caches - one for each type - but (a) I'm not sure how to do this and (b) I've read somewhere that it's not recommended to use multiple caches. Any ideas?
Yeah, I've had the same issue before and resorted to creating an extended version of the MemoryCache that allows me to plug in different "stores".. You can do it simply by wrapping the data you're sticking into the cache in a "metadata" type class. I suppose similar to how the ServiceDescriptors wrap your service registrations in the DI?
Also, in specific answer to the point "I thought about using two caches - one for each type". This is where the problem will arise because I believe IMemoryCache gets registered as a singleton by default
I ran into this problem myself. One solution I thought of was to just two instantiate separate memory caches in a wrapper class and register the wrapper class as a singleton instance. However, this only makes sense if you have different requirements for each memory cache and/or you expect to store a massive amount of data for each memory cache (at that point, an in-memory cache may not be what you want).
Here is some example classes I want to cache.
// If using a record, GetHashCode is already implemented through each member already
public record Person(string Name);
// If using a class, ensure that Equals/GetHashCode is overridden
public class Car
{
public string Model { get; }
public Car(string model)
{
Model = model;
}
public override bool Equals(object? obj)
{
return obj is Car car &&
Model == car.Model;
}
public override int GetHashCode()
{
return HashCode.Combine(Model);
}
}
Here is a dual MemoryCache implementation.
public class CustomCache : ICustomCache // Expose what you need and register it as singleton instance
{
private readonly MemoryCache personCache;
private readonly MemoryCache carCache;
public CustomCache(IOptions<MemoryCacheOptions> personCacheOptions, IOptions<MemoryCacheOptions> carCacheOptions)
{
personCache = new MemoryCache(personCacheOptions);
carCache = new MemoryCache(carCacheOptions);
}
public void CreatePersonEntry(Person person)
{
_ = personCache.Set(person, person, TimeSpan.FromHours(1));
}
public void CreateCarEntry(Car car)
{
_ = carCache.Set(car, car, TimeSpan.FromHours(12));
}
}
If you don't have the above requirements, then you could just do what juunas mentioned and create an easy wrapper with a composite key. You still need to ensure GetHashCode is properly implemented for each class you want to store. Here, my composite key is just an integer (I used prime numbers, no specific reason) paired with an object. I didn't use a struct for the key as the MemoryCache uses a Dictionary<object, CacheEntry>, so I don't want to box/unbox the key.
public class CustomCache : ICustomCache // Expose what you need
{
private readonly IMemoryCache cache;
public CustomCache(IMemoryCache cache)
{
this.cache = cache;
}
public void CreatePersonEntry(Person person)
{
_ = cache.Set(CustomKey.Person(person), person, TimeSpan.FromHours(1));
}
public void CreateCarEntry(Car car)
{
_ = cache.Set(CustomKey.Car(car), car, TimeSpan.FromHours(12));
}
private record CompositeKey(int Key, object Value)
{
public static CustomKey Person(Person value) => new(PERSON_KEY, value);
public static CustomKey Car(Car value) => new(CAR_KEY, value);
private const int PERSON_KEY = 1123322689;
private const int CAR_KEY = 262376431;
}
}
Let me know if you see anything wrong, or if there is a better solution.
Background: I found "dysfunctional" code in spring-admin project: "Cannot construct instance of Registration (no Creators, like default construct, exist)". So I wrote custom deserializer and report the issue. But report was rejected, since it allegedly works. And after retest it seems to work now. Does not make sense. So I would like to know why that code work.
But here is the catch. When I wrote similar test class, it does not work in my project. Even when I literally take the code of "now-working" Registration class, and try it in own project, is simply does not deserialize. And then, with practically identical class, it works. It doesn't make any sense.
https://github.com/codecentric/spring-boot-admin/blob/master/spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/domain/values/Registration.java
Following post explains how lombok-jackson combo works, but it does not work here. I'm totally confused, this is unbelievelably ridiculous situation, where (unnecessary) simplification creates superb complexity. But I'd like to understand it, since I can encounted this situation in future again.
Jackson Deserialization Fails because of non-default constructor created by lombok
So to have something easy to work with: here we have nice&working pure jackson:
public class TestTO_pureJackson {
private final String a;
private final String b;
#JsonCreator
private TestTO_pureJackson(#JsonProperty("a") String a, #JsonProperty("b") String b) {
this.a = a;
this.b = b;
}
}
and here we have not working lombok equivalent (even if I remove one field, so that it's "same" to latter example):
#lombok.Data
public class TestTO {
private final String a;
private final String b;
#lombok.Builder(builderClassName = "Builder")
private TestTO(String a, String b) {
this.a = a;
this.b = b;
}
public static TestTO.Builder create(String a) {
return builder().a(a);
}
}
and we are trying to deserialize:
{"a": "a", "b": "b"}
Can anyone understand the magic under the hood, and help me to understand what's wrong here?
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
And to make it even more ridiculous (do you actually see any significant difference with TestTO???), following code works:
#lombok.Data
public class Pair {
private final String left;
private final String right;
#lombok.Builder(builderClassName = "Builder")
private Pair(String pairId) {
left = pairId.substring(0, 3).toUpperCase(Locale.US);
right = pairId.substring(3).toUpperCase(Locale.US);
}
}
and main method:
public class PairTest {
public static final String DATA = "[\"btcusd\",\"ltcusd\",\"ltcbtc\"]";
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Pair[] pairs = objectMapper.readValue(DATA, Pair[].class);
for (Pair pair : pairs) {
System.out.println(pair);
}
}
}
Can anyone see, why 2 almost same TO classes behave differently?
TestTO does not work because there is no constructor that Jackson can use. It cannot use the two-args constructor, because it does not know which JSON field should be used for which argument (because argument names are removed during compilation).
For lombok-generated constructors, you can work around that by advising Lombok to generate a #ConstructorProperties annotation. Just add
lombok.anyConstructor.addConstructorProperties=true
to your lombok.config. In your case of a manual constructor, you could also simply add the #JsonPropertys.
(Note that Jackson does not automatically use builders; you have to explicitly tell Jackson that with #JsonDeserialize and #JsonPOJOBuilder.)
TestTO_pureJackson works, because #JsonProperty is available at runtime and used by Jackson to determine the mapping.
Pair works, because there is a usable constructor: Jackson does not have to guess which parameter belongs to which field, because there is just one. Note that this only works for String, int, long or boolean one-arg constructors.
Lombok does not generate any additional constructor (here: the two-args constructor) if there is already one (see documentation of #Data), so this is the only constructor on the class.
I have a class that we use for paginated results, as follows:
public class PaginatedList<T> extends LinkedList<T> {
private int offset;
private int count;
private int totalResultCount;
//...
}
and I'd like Jackson to serialize it like this:
{
"results":[1,2,3],
"offset":0,
"count":3,
"totalResultCount":15
}
(where the parent list contains the three integer values 1,2 and 3.)
In my first attempt I discovered that Jackson effectively ignores any properties on classes which are assignable to a Collection class. In hindsight, this makes sense, and so I'm now in search of a workaround. A search of SO resulted in two similar questions:
jackson-serialization-includes-subclasss-fields
jaxb-how-to-serialize-fields-in-a-subclass-of-a-collection
However, both of these resulted in the suggestion to switch from inheritance to composition.
I am specifically looking for a solution that allows the class to extend a collection. This 'PaginatedList' class is part of the common core of the enterprise, and extends Collection so that it can be used (and introspected) as a collection throughout the code. Changing to composition isn't an option. That being said, I am free to annotate and otherwise change this class to support serialization as I described above.
So, from what I can tell, there's two parts I'm missing (what I'm looking for in an answer):
How to get Jackson to 'see' the added properties?
How to get Jackson to label the collection's content as a 'results' property in the JSON output?
(PS: I'm only concerned with serialization.)
Ashley Frieze pointed this out in a comment, and deserves the credit for this answer.
I solved this by creating a JsonSerializer instance as follows:
public class PaginatedListSerializer extends JsonSerializer<PaginatedList> {
#Override
public Class<PaginatedList> handledType() {
return PaginatedList.class;
}
#Override
public void serialize(PaginatedList value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeArrayFieldStart("results");
for (Object entry : value) {
jgen.writeObject(entry);
}
jgen.writeEndArray();
jgen.writeNumberField("offset", value.offset);
jgen.writeNumberField("count", value.count);
jgen.writeNumberField("totalResultCount", value.totalResultCount);
jgen.writeEndObject();
}
}
and, of course, register it as a module:
SimpleModule testModule = new SimpleModule("PaginatedListSerializerModule", new Version(1, 0, 0, null, null, null));
testModule.addSerializer(new PaginatedListSerializer());
mapper.registerModule(testModule);
I am getting an error : i am using entity framework, wcf.
Error:cannot implicitly convert type System.linq.iorderedQueryable<xDataModel.Info> to System.Collection.Generic.List<xServiceLibrary.Info>
Below are my code:
WCF Service:
namespace xServiceLibrary
{
public List<Info> GetScenario()
{
xEntities db = new xEntities();
var query = from qinfo in db.Infoes
select qinfo;
//return query.Cast<Info>().ToList(); (not working)
//return query.toList(); (not working)
return query;
}
}
Interface:
namespace xServiceLibrary
{
[OperationContract]
List<Info> GetScenario();
}
Class:
namespace xServiceLibrary
{
[DataContract]
public class Info
{
[DataMember]
public int Scenario_Id;
[DataMember]
public string Scenario_Name { get; set; }
[DataMember]
public string Company_Name { get; set; }
}
}
update:(2)
I have two class library files.
One is xDataModel namespace in which i have created xmodel.edmx file.
second is xServiceLibrary namespace where i am implementing Wcf Service.
i have attached the xDataModel.dll file in my xServiceLibrary so that i could query my EF Model.
i am not able to understand the concept. any help would be appreciated.
The problem is that you have two different types named Info: DataModel.Info and ServiceLibrary.Info - because these are different types you cannot cast one into the other.
If there is no strong reason for both being there I would eliminate one of them. Otherwise as a workaround you could project DataModel.Info to ServiceLibrary.Info by copying the relevant properties one by one:
var results = (from qinfo in db.Infoes
select new ServiceLibrary.Info()
{
Scenario_Id = qinfo.Scenario_Id,
//and so on
}).ToList();
The problem is that you have two different classes, both called Info, both in scope at the time you run your query. This is a very very bad thing, especially if you thought they were the same class.
If DataModel.Info and ServiceLibrary.Info are the same class, you need to figure out why they are both in scope at the same time and fix that.
If they are different classes, you need to be explicit about which one you are trying to return. Assuming that your EF model includes a set of DataModel.Info objects, your options there are:
Return a List<DataModel.Info> which you can get by calling query.ToList()
Return a List<ServiceLibrary.Info> which you can get by copying the fields from your DataModel.Info objects:
var query = from qinfo in db.Info
select new ServiceLibrary.Info
{
Scenario_Id = q.Scenario_Id,
Scenario_Name = q.Scenario_Name
Company_Name = q.Company_Name
};
Return something else, such as your custom DTO object, similar to #2 but with only the specific fields you need (e.g. if ServiceLibrary.Info is a heavy object you don't want to pass around.
In general, though, your problem is centered around the fact that the compiler is interpreting List<Info> as List<ServiceLibrary.Info> and you probably don't want it to.
take a look at this example code:
public class Comment
{
private Comment()
{ }
public Comment(string text, DateTime creationDate, string authorEmail)
{
Text = text;
CreationDate = creationDate;
AuthorEmail = authorEmail;
}
public virtual string Text { get; private set; }
public virtual DateTime CreationDate { get; set; }
public virtual string AuthorEmail { get; private set; }
}
i know it's considered bad practice to call virtual member functions from the constructor, however in NHibernate i need the properties to be virtual to support lazy loading. Is it considered OK in this case?
I'm pretty sure this is fine, but if your worried you could always just assign the properties after a parameter less constructor call.
To expand on Paco's answer:
In most cases it doesn't hurt. But if the class is inherited, virtual allows the properties get/set to be overriden, so the behavior is no longer fully encapsulated and controlled, so it can break, in theory. FxCop warns about this because it's a potential problem.
The point of FxCop is to help warn you about potential problems though. It is not wrong to use properties in a constructor if you know you who/what is ever going to inherit from the class, but it isn't officially 'best practice'.
So, the answer is that it's fine as long as you control any inheritence of the class. Otherwise, don't use it and set the field values directly. (Which means you can't use C# 3.0 automatic get/set properties--you'll have to write properties wrapping fields yourself.)
Side note: Personally, all of my projects are web sites that we host for clients. So assuming this setup stays the same for a project, than it's worth the trade-off of having to duplicate the various null/argument checking. But, in any other case where I am not sure that we'll maintain complete control of the project and use of the class, I wouldn't take this shortcut.
It's OK in this sample, but it might cause problems when you inherit the class and override the properties. Generally, you can better create fields for the virtual properties.
IMHO the best-practice is to use properties with backing fields:
public class Comment
{
private DateTime _creationDate;
private string _text;
private string _authorEmail;
private Comment() { }
public Comment(string text, DateTime creationDate, string authorEmail)
{
_text = text;
_creationDate = creationDate;
_authorEmail = authorEmail;
}
public virtual string Text
{
get { return _text; }
private set { _text = value; }
}
public virtual string AuthorEmail
{
get { return _authorEmail; }
private set { _authorEmail = value; }
}
public virtual DateTime CreationDate
{
get { return _creationDate; }
set { _creationDate = value; }
}
}
So you can avoid problems on child classes and you don't see any warning anymore
I know that FxCop complains if you call a virtual method in your constructor, but I don't know what FxCop says whether you're calling a virtual property in your constructor ...
I would think that FxCop will complain as well since a property is translated to a method in IL.
You can also create your properties as 'non-virtual', and just specify 'lazy=false' on your 'class mapping' in NHIbernate.
This won't affect the lazy-load behavior of collections.
(I do it all the time, since I do not like that my infrastructure (NHibernate) requires me to have the properties virtual.
I also don't know whether the performance benefit of having dynamic proxies in NHibernate is significant).
I think, you should not call it in the constructor.
You can provide a method Initialize() which you can call after constructing the object.
In Initialize() you can call the required virtual methods