System.Text.Json.JsonSerializer doesn't serialize properties from derived classes - .net-core

I can't have System.Text.Json.JsonSerializer serializing properties from derived classes. Newtonsoft has no issues doing the job.
namespace TestJson
{
class Program
{
static void Main(string[] args)
{
var myClass = new NewClass2
{
Value1 = "qsd",
ShouldBeSerialized = "why am I not serialized?"
};
// as expected, getting {"ShouldBeSerialized":"why am not serialized?","Value1":"qsd"}
var textJsonSerializedFromTop = System.Text.Json.JsonSerializer.Serialize<NewClass2>(myClass);
// expecting {"ShouldBeSerialized":"why am not serialized?","Value1":"qsd"}
// but getting {"Value1":"qsd"}
var textJsonSerializedFromBase = System.Text.Json.JsonSerializer.Serialize<ClassBase>(myClass);
// as expected, getting {"ShouldBeSerialized":"why am not serialized?","Value1":"qsd"}
var newtonSoftSerializedFromBase = Newtonsoft.Json.JsonConvert.SerializeObject(myClass);
}
}
public class ClassBase
{
public string Value1 { get; set; }
}
public class NewClass2 : ClassBase
{
public string ShouldBeSerialized { get; set; }
}
}
In the previous example to show the actual issue, the solution is straight forward, but in the following one, I have no clue how to manage by not using newtonsoft:
namespace TestJson
{
class Program
{
static void Main(string[] args)
{
var toSerialize =
new[] {
new ClassBase
{
Value1 = "aze"
},
new NewClass2
{
Value1="qsd",
ShouldBeSerialized="why am I not serialized?"
}
};
var textJsonSerialized = System.Text.Json.JsonSerializer.Serialize(toSerialize);
// expecting [{"Value1":"aze"},{"ShouldBeSerialized":"why am not serialized?","Value1":"qsd"}]
// but getting [{"Value1":"aze"},{"Value1":"qsd"}]
Console.WriteLine(textJsonSerialized);
var newtonSoftSerialized = Newtonsoft.Json.JsonConvert.SerializeObject(toSerialize);
// as expected getting [{"Value1":"aze"},{"ShouldBeSerialized":"why am not serialized?","Value1":"qsd"}]
Console.WriteLine(newtonSoftSerialized);
}
}
public class ClassBase
{
public string Value1 { get; set; }
}
public class NewClass2 : ClassBase
{
public string ShouldBeSerialized { get; set; }
}
}

Related

“How to fix ‘The instance of entity type ' cannot be tracked because another instance with the key value '{TypeId: 1}' is already being tracked.

The instance of entity type 'WalletType' cannot be tracked because another instance with the key value '{TypeId: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
//WalletType.cs
public class WalletType
{
public WalletType()
{
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int TypeId { get; set; }
[MaxLength(150)]
public string TypeTitle { get; set; }
public virtual ICollection<Wallet> Wallets { get; set; }
}
////////////////////////////////
//SeedData.cs
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService>()))
{
// Look for any movies.
if (context.WalletTypes.Any())
{
return; // DB has been seeded
}
context.WalletTypes.AddRange(
new WalletType
{
TypeId = 1,
TypeTitle = "function1"
},
new WalletType
{
TypeId = 1,
TypeTitle = "function2"
}
);
context.SaveChanges();
}
}
}
///////////////////////////////////////
//Program.cs
public class Program
{
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.
GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
SeedData.Initialize(services);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
That's because you add to WalletType with the dame TypeId.
You can either set identity to auto or provide unique value manually.
context.WalletTypes.AddRange(
new WalletType
{
TypeId = 1,
TypeTitle = "function1"
},
new WalletType
{
TypeId = 2,
TypeTitle = "function2"
}
);

Sequence with CodeActivities with WorkflowInvoker's input arguments doesn't work?

Why is it not possible to pass the arguments to CodeActivity via WorkflowInvoker's input dictionary, if the activities are within a Sequence? The WorkflowInvoker.Invoke(sequence, dict) method throws the following exception:
Additional information: The values provided for the root activity's arguments did not satisfy the root activity's requirements:
'Sequence': The following keys from the input dictionary do not map to arguments and must be removed: Arg. Please note that argument names are case sensitive.
class Program
{
static void Main(string[] args)
{
var sequence = new Sequence();
var start = new Start();
var end = new End();
sequence.Activities.Add(start);
sequence.Activities.Add(end);
var dict = new Dictionary();
dict["Arg"] = "Debug text.";
WorkflowInvoker.Invoke(sequence, dict);
}
}
public class Start : CodeActivity
{
public InArgument Arg { get; set; }
protected override void Execute(CodeActivityContext context)
{
Debug.WriteLine(Arg.Get(context));
}
}
public class End : CodeActivity
{
public InArgument Arg { get; set; }
protected override void Execute(CodeActivityContext context)
{
Debug.WriteLine(Arg.Get(context));
}
}
// ************** Second example with custom sequence *************************
class Program
{
static void Main(string[] args)
{
var seq = new MySequence();
seq.Activities.Add(new Last());
seq.Activities.Add(new First());
var dict = new Dictionary();
dict["Arg"] = "Text";
WorkflowInvoker.Invoke(seq, dict);
}
}
public class First : CodeActivity
{
public InArgument Arg { get; set; }
protected override void Execute(CodeActivityContext context)
{
var val = Arg.Get(context);
}
}
public class Last : CodeActivity
{
public InArgument Arg { get; set; }
protected override void Execute(CodeActivityContext context)
{
var val = Arg.Get(context);
}
}
public class MySequence : NativeActivity
{
public InArgument Arg { get; set; }
public Collection Activities = new Collection();
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
base.CacheMetadata(metadata);
metadata.SetChildrenCollection(Activities);
}
protected override void Execute(NativeActivityContext context)
{
foreach (var activity in Activities)
context.ScheduleActivity(activity);
}
}
The code activities take their arguments from the container they are in not the input dictionary. The container needs to have an in argument matching the one in the dictionary.
Sequences don't accept arguments so you wrap them in an Activity.
An Activity constructed as below is a worklfow
public class MyCodeWorkflow : Activity
{
public InArgument<string> inMSG { get; set; }
public OutArgument<string> outMSG { get; set; }
public MyCodeWorkflow()
{
this.Implementation = () => new Sequence {
Activities =
{
new WriteLine
{
Text=new InArgument<string>((activityContext)=>this.inMSG.Get(activityContext))
},
new Assign<string>
{
To=new ArgumentReference<string>("outMSG"),
Value=new InArgument<string>
(
(activityContext)=>this.inMSG.Get(activityContext)
)
}
}
};
}
}
//host
static void Main(string[] args)
{
IDictionary<string, object> input = new Dictionary<string, object>();
input.Add("inMSG","hello");
IDictionary<string, object> output = new Dictionary<string, object>();
MyCodeWorkflow activity = new MyCodeWorkflow();
output = WorkflowInvoker.Invoke(activity,input);
Console.WriteLine(output["outMSG"]);
}
The code above was taken from http://xhinker.com/post/WF4Authoring-WF4-using-imperative-code%28II%29.aspx

How to show sum using LINQ statement in Grid view of the MVC app

I developing the MVC application.
I am stuck in LINQ Syntax.
I wan to show the sum of List Items in index view of parent.
Please check code below.
In Model I have two classes.
public class StockAdjustment
{
public int Id { get; set; }
public List<StockAdjustmentItem> StockAdjustmentItems { get; set; }
public int SumOfStockAdjustmentItemQuantity
{
get
{
if (this.StockAdjustmentItems != null)
{
return this.StockAdjustmentItems.Sum(s=>s.OriginalQuantity);
}
return 0;
}
}
}
public class StockAdjustmentItem
{
public int Id { get; set; }
public int OriginalQuantity { get; set; }
public StockAdjustment StockAdjustment { get; set; }
}
public StockAdjustment GetAll(int Id)
{
oStockAdjustment = GetStockAdjustmentById(Id);
var prepo = new ProductRepo();
oStockAdjustment.StockAdjustmentItems = new List<StockAdjustmentItem>();
StockAdjustmentItem ai1 = new StockAdjustmentItem();
ai1.Id = 1 ;
ai1.OriginalQuantity = 250;
oStockAdjustment.StockAdjustmentItems.Add(ai1);
StockAdjustmentItem ai2 = new StockAdjustmentItem();
ai2.Id = 1;
ai2.OriginalQuantity = 375;
oStockAdjustment.StockAdjustmentItems.Add(ai2);
return oStockAdjustment;
}
Now I have controller Code
public ActionResult Index(string searchContent = "")
{
AdjustmentRepo oAdjustmentRepo = new AdjustmentRepo();
var adjustments = from adjustment in oAdjustmentRepo.GetAll() select adjustment;
ViewBag.StockAdjustmentList = adjustments;
return View(adjustments);
}
This Working perfectly fine...
Now, the problem comes when, I am trying to show StockAdjustment in List.
I have to show the sum of the OriginalQuantites of StockAdjustmentItems in the Front of StockAdjustment item in grid.
in above Exmaple I want to show 650(250 + 375) in the row of a gird.
#model IEnumerable<StockWatchServices.DomainClass.StockAdjustment>
#Html.Grid(Model).Columns(columns =>
{
columns.Add(c=>c.StockAdjustmentItems.Sum( OriginalQuantity ???? Im stuck here... )
}
What should I write here ?
I can see like this...
Create a getter only property on the StockAdjustment class
public class StockAdjustment
{
public int Id { get; set; }
public List<StockAdjustmentItem> StockAdjustmentItems { get; set; }
public int SumOfStockAdjustmentItemQuantity
{
get
{
if (this.StockAdjustmentItems != null)
{
return this.StockAdjustmentItems.Sum(s=>s.OriginalQuantity);
}
return 0;
}
}
}
And then in your Razor view:
#Html.Grid(Model).Columns(columns =>
{
columns.Add(c => c.SumOfStockAdjustmentItemQuantity)
}
Can you try with below code :
#Html.Grid(Model).Columns(columns =>
{
columns.Add(c => c.StockAdjustmentItems.Where(quantity => quantity.OriginalQuantity != null).Sum(sum => sum.OriginalQuantity).ToString());
})

Steps to map classes using ValueInjector

Quickly getting to the problem the mapping does not occur for the following code. Could someone explain why? or what i should do for the mapping to occur?
var parent = new Parent();
parent.ChildOne.Add(new ChildOne() { Name = "Child One" });
parent.ChildTwo.Add(new ChildTwo() { Name = "Child Two" });
AnotherParent anotherParent = new AnotherParent();
anotherParent.InjectFrom<LoopValueInjection>(parent);
Required Class are below
Anothher child one
public class AnotherChildOne
{
public string Name { get; set; }
}
Another child two
public class AnotherChildTwo
{
public string Name { get; set; }
}
Another Parent
public class AnotherParent
{
public ICollection<AnotherChildOne> ChildOne { get; set; }
public ICollection<AnotherChildTwo> ChildTwo { get; set; }
public AnotherParent()
{
ChildOne = new Collection<AnotherChildOne>();
ChildTwo = new Collection<AnotherChildTwo>();
}
}
Child Two
public class ChildTwo
{
public string Name { get; set; }
}
Child One
public class ChildOne
{
public string Name { get; set; }
}
Parent
public class Parent
{
public ICollection<ChildOne> ChildOne { get; set; }
public ICollection<ChildTwo> ChildTwo { get; set; }
public Parent()
{
ChildOne = new Collection<ChildOne>();
ChildTwo = new Collection<ChildTwo>();
}
}
I believe by default Value Injector will only inject the properties with the same name of the same type. You can get around this using a tweak to the CloneInjection sample from the Value Injector documentation as described here with this code:
public class CloneInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
}
protected override object SetValue(ConventionInfo c)
{
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string)
|| c.TargetProp.Type.IsValueType || c.TargetProp.Type == typeof(string))
return c.SourceProp.Value;
//handle arrays
if (c.SourceProp.Type.IsArray)
{
var arr = c.SourceProp.Value as Array;
var clone = Activator.CreateInstance(c.TargetProp.Type, arr.Length) as Array;
for (int index = 0; index < arr.Length; index++)
{
var a = arr.GetValue(index);
if (a.GetType().IsValueType || a.GetType() == typeof(string)) continue;
clone.SetValue(Activator.CreateInstance(c.TargetProp.Type.GetElementType()).InjectFrom<CloneInjection>(a), index);
}
return clone;
}
if (c.SourceProp.Type.IsGenericType)
{
//handle IEnumerable<> also ICollection<> IList<> List<>
if (c.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
var t = c.TargetProp.Type.GetGenericArguments()[0];
if (t.IsValueType || t == typeof(string)) return c.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
var list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach (var o in c.SourceProp.Value as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(o);
addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e);
}
return list;
}
//unhandled generic type, you could also return null or throw
return c.SourceProp.Value;
}
//for simple object types create a new instace and apply the clone injection on it
return Activator.CreateInstance(c.TargetProp.Type)
.InjectFrom<CloneInjection>(c.SourceProp.Value);
}
}
If you include the above CloneInjection code you will want to do this:
anotherParent.InjectFrom<CloneInjection>(parent);
instead of:
anotherParent.InjectFrom<LoopValueInjection>(parent);

Nunit tests on Response.Cache.VaryByHeader

I am doing some Unit testing with NUnit and NSubstitute on a function that uses HttpResponse, I know you can't mock these objects so I have created interfaces to represent them and some of there properties.
I'm having trouble understanding how to create an interface for Response.Cache.VaryByHeader
// This is my HttpResponse interface
public interface IHttpResponse
{
Stream Filter { get ; set; }
IHttpCachePolicy Cache { get; set; }
void AppendHeader(string name, string value);
}
// concrete httresponse
public class HttpResponseProxy : IHttpResponse
{
private HttpResponse _httpResponse;
public Stream Filter {
get {
return _httpResponse.Filter ?? new MemoryStream();
}
set { _httpResponse.Filter = value; }
}
public IHttpCachePolicy Cache
{
get { return new HttpCachePolicyProxy(_httpResponse.Cache); }
set { }
}
public HttpResponseProxy(HttpResponse httpResponse)
{
if (httpResponse == null)
{
throw new ArgumentNullException("httpResponse");
}
_httpResponse = httpResponse;
_httpResponse.Filter = httpResponse.Filter;
}
public void AppendHeader(string name, string value)
{
_httpResponse.AppendHeader(name, value);
}
}
// HttpCachePolicy interface
public interface IHttpCachePolicy
{
IHttpCacheVaryByHeaders VaryByHeaders { get; set; }
}
// concrete HttpCachePolicy
public class HttpCachePolicyProxy : IHttpCachePolicy
{
private HttpCachePolicy _httpCachePolicy;
public HttpCachePolicyProxy(HttpCachePolicy httpCachePolicy)
{
_httpCachePolicy = httpCachePolicy;
}
public IHttpCacheVaryByHeaders VaryByHeaders
{
get { return new HttpCacheVaryByHeadersProxy(_httpCachePolicy.VaryByHeaders as HttpCacheVaryByHeaders); }
set { }
}
}
public interface IHttpCacheVaryByHeaders
{
IHttpCacheVaryByHeaders HttpCacheVaryByHeaders { get; set; }
}
public class HttpCacheVaryByHeadersProxy : IHttpCacheVaryByHeaders
{
private HttpCacheVaryByHeaders _httpCacheVaryByHeaders;
public HttpCacheVaryByHeadersProxy(HttpCacheVaryByHeaders httpCacheVaryByHeaders)
{
_httpCacheVaryByHeaders = httpCacheVaryByHeaders;
}
public IHttpCacheVaryByHeaders HttpCacheVaryByHeaders
{
get { return new HttpCacheVaryByHeadersProxy(_httpCacheVaryByHeaders); }
set { }
}
}
This is the function i am actually testing:
public static void CompressPage(IHttpRequestGetCompressionMode getCompressionMode, IHttpResponse httpResponse)
{
string supportedCompression = getCompressionMode.GetClientSupportedCompressionMode();
if (supportedCompression != HttpHeaderValues.NoCompression)
{
switch (supportedCompression)
{
case HttpHeaderValues.DeflateCompression:
httpResponse.Filter = new DeflateStream(httpResponse.Filter, CompressionMode.Compress);
break;
case HttpHeaderValues.GZipCompression:
httpResponse.Filter = new GZipStream(httpResponse.Filter, CompressionMode.Compress);
break;
}
httpResponse.AppendHeader(HttpHeaderValues.ContentEncodingHeader, supportedCompression);
// this line is where i have the problem
httpResponse.Cache.VaryByHeaders[HttpHeaderValues.AcceptEncodingHeader] = true;
}
}
I'm getting "cannot apply index to an expression of type IHttpCacheVaryByHeaders" errors.
I have the interface for the response and cache but how do I represent VaryByHeaders in an interface and then use it in a concrete class?
The error seems to suggest that IHttpCacheVaryByHeaders does not have an indexer declared (e.g. bool this[string header] { get; set; }), but rather than implementing these wrappers yourself, try the HttpResponseWrapper and other System.Web.Abstractions. This will should make testing this stuff a lot easier. :)

Resources