Convert Entity Framework Object to JSON Object - c#

I'm trying to make a generic handler post a JSONJ object based on my entity type SYSTEM_AUDIT_SHEET:
SYSTEM_AUDIT_SHEET sheet = ctx.SYSTEM_AUDIT_SHEET
.Where(s => s.SYSTEM_KEY == system_key_dec)
.Select(s => s)
.OrderByDescending(s => s.AUDIT_SHEET_VERSION)
.First();
HttpContext.Current.Response.Write(serializer.Serialize(sheet));
But I get the following error:
A circular reference was detected while serializing an object of type
'System.Data.Entity.DynamicProxies.SYSTEM_AUDIT_SHEET_521A7B786A51FC0F83641182DD72C8DFE6C082418D30BBB977B403409A74CE17'.
Why can't I convert the entity to JSON?

You cannot convert objects to json that reference themselves as this would create an infinitely long json string.
For example, the following pseudo-code wouldn't work because it sets up a circular reference (Dog >> Bone >> Dog...):
class Dog {
private Bone myBone;
public Dog() {
myBone = new Bone(this);
}
}
class Bone {
private Dog buriedBy;
public Bone(Dog d) {
buriedBy = d;
}
}
There seem to be some good solutions by googling 'json circular reference'. See the top two stack overflow links.

The problem is probably that your SYSTEM_AUDIT_SHEET either contains a property that references instances of type SYSTEM_AUDIT_SHEET, or it contains a property that points to objects that have pointers to SYSTEM_AUDIT_SHEET instances. Serializing such a circle of pointers would result in a serialization process that would never end.
You will need to transform your SYSTEM_AUDIT_SHEET to a type that does not (directly or indirectly) reference itself before doing the serialization. You can create a brand new type and write code to instantiate such a type from your SYSTEM_AUDIT_SHEET (AutoMapper might come in handy here). However, I tend to find that in most cases it is easier to just use an anonymous type:
SYSTEM_AUDIT_SHEET sheet = /*some sheet*/
var json = new {
sheet.Id,
sheet.RevisionNumber,
sheet.Title
};
return serializer.Serialize(json);
EDIT
If you want to use AutoMapper and assuming that your sheet looks something like
class SYSTEM_AUDIT_SHEET
{
public int Id { get; set; }
public SYSTEM_AUDIT_SHEET SomeOtherAuditSheet { get;set;}
public string Title { get;set;}
}
you could create a type like
class JSON_SYSTEM_AUDIT_SHEET
{
public int Id { get; set; }
public int SomeOtherAuditSheetsId { get;set;}
public string Title { get;set;}
}
When your application starts (say, in Application_Start) you configure AutoMapper:
AutoMapper.Mapper.CreateMap<SYSTEM_AUDIT_SHEET, JSON_SYSTEM_AUDIT_SHEET>()
.ForMember(dest => dest.SomeOtherAuditSheetsId, opt => opt.MapFrom(src => src.SomeOtherAuditSheet.Id));
The Id and Title properties will be mapped directly across from SYSTEM_AUDIT_SHEET to JSON_SYSTEM_AUDIT_SHEET because they have the same names in both types. The property SomeOtherAuditSheetsId needs special configuration, because there is no property with that exact name on the source type.
When you want to convert SYSTEM_AUDIT_SHEET to JSON_SYSTEM_AUDIT_SHEET you do:
return AutoMapper.Mapper.Map<SYSTEM_AUDIT_SHEET , JSON_SYSTEM_AUDIT_SHEET >(sheet);
You may want to have a look at AutoMapper's flattening features.
Hope this helps.

Related

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);
}
}

Trying to convert an C# anonymous type to a strong type

I'm trying to convert some anonymous type back to its original strong type class.
I have some legacy code (which I cannot touch) which create an anonymous class:
public class Cat : FooId
{
public int Id { get; set; }
public string Name { get; set; }
}
var result = new
{
Id = Mapper.Map<TFooId>(someCat)
};
NOTE: I've tried to make this fake class and interface similar to my code.
This then gives me:
result.GetType().ToString() : <>f__AnonymousType1``1[MyProject.Cat]
From here, I'm not sure how to convert this back to a MyProject.Cat instance?
I've tried (and fails):
(MyProject.Cat)result
(dynamic)result
but both fail. The dynamic doesn't throw an error ... but I can't access any properties in it.
C# is a statically typed language, and those two types are not in any way related to one another. Unless you're able to modify the code which defines those types, the way you'd convert from one to the other would be to create a new instance of the target type and populate it from the source object.
For example:
var resultCat = new Cat { Id = result.Id };
Edit: From comments it looks like it may be possible that the Id property on the result object may be an instance of Cat or some other object? You're going to need to do some debugging to find out what your types are.
But the overall concept doesn't really change. If you have an instance of Cat in your results then you can use that instance. If you don't then in order to create one you'd need to create a new instance and populate it with the data you have. Even if two types are intuitively or semantically similar, they are different types.
It's true what David said with regard to the fact that C# is a statically-typed language and that the new instance should be populated from the source the way he suggested.
However, there are work-arounds (though less performant) for that, such as reflection.
Consider you have a console app where you have defined ObjectExtensions as follows:
public static class ObjectExtensions
{
public static TOut Map<TOut>(this object #in)
where TOut : new()
{
TOut #out = new TOut();
if (#in?.GetType() is Type tin)
{
Type tout = typeof(TOut);
foreach ((PropertyInfo pout, PropertyInfo pin) in tout.GetProperties().Join(tin.GetProperties(), pi => pi.Name, pi => pi.Name, (pout, pin) => (pout, pin)))
{
pout.SetValue(#out, pin.GetValue(#in));
}
}
return #out;
}
}
And Class1 as follows:
public class Class1
{
public string A { get; set; } = "A";
public string B { get; set; } = "B";
public string C { get; set; } = "C";
public override string ToString()
{
return $"{{A={A}, B={B}, C={C}}}";
}
}
You will be able to map your anonymous type back to its original strongly-typed class like this:
Console.WriteLine(new { A = "Anonymous A", B = "Anonymous B", C = "Anonymous C" }.Map<Class1>());
Therefore the bloc above should show the following output:
{A=Anonymous A, B=Anonymous B, C=Anonymous C}
In this case, of course, I have assumed that Class1 (Cat in your example) must have a public parameterless constructor. That may not always be the case. There are more sophisticated scenarios of course that might involve other techniques for creating the object such as cloning or dependency injection. Just saying that the idea of yours is possible.

How to configure a mapping of collection types with AutoMapper?

For example, I've implemented two classes like these:
public class A
{
public List<C> Items { get; set; }
}
public class B
{
public IImmutableList<C> Items { get; set; }
}
public class C
{
}
When I try to map A to B and vice versa, I get an exception because List<string> cannot be converted to IImmutable<string>.
Probably I could provide a mapping for A<->B, but since it'll be a very common pattern in my solution, I'd like to avoid to manually mapping each class that may fall into the same case.
Is there anyway I can generalize the whole mapping using generic type definitions from a collection type to another collection type?
This is what I want to avoid
mapperConfig.CreateMap<A, B>()
.ForMember(a => a.Items, opts => opts.Ignore())
.AfterMap
(
(source, target) =>
{
target.Items = source.Items.ToImmutableList();
}
);

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);
}
}
}

C# Automapper Nested Object

Ive searched for this for few days now and cant seem to get anything to work, I am using c# MVC Entity Framework with Automapper and im trying to achieve the below ViewModels (mainly LostDocumentVM) to be mapped from my database, all other properties will be set in controllers.
Here is my ViewModels...
DocumentVM
{
Public Enum.HistoricType HistoricType {get;set;}
Public DocumentChildVM Document { get; set;}
}
DocumentChildVM
{
Public bool ShowHistoricLink {get;set;}
Public IEnumerable<ListDocumentVM> DocumentsToReview {get;set;}
}
ListDocumentVM
{
Public int Id {get;set;}
Public string Name {get; set;}
Public DateTime? ReviewDate {get;set;}
}
I initialise the DocumentVM like this...
DocumentVM documentVM = DataContext.SystemUser.Where(x=>x.SustemUserID==LoggedOnUserID).Project().To<DocumentVM>().SingleOrDefault();
And my mapping is like this...
Mapper.CreateMap<SystemUser,DocumentVM>()
.ForMember(dest=>dest.Document.DocumentsToReview, opt=>opt.MapFrom(src=>src.Documents.Where(x=>x.DocumentType == Enum.DocumentType.Assessment));
Im new to AutoMapper and struggling to get more advanced mappings to work.
Yes, your ForMember member must refer to a member on the destination type, and yours is referring to a member on the child type. Instead, you'll need to create an AfterMap function that fills in this information on that child entity.
It's not difficult, but you have a bit of a strange set up where a child object Document has a property DocumentsToReview from another property on the parent DocumentVM:
documentVM.Document.DocumentsToReview =
src.Documents.Where(doc => doc.DocumentType == Enum.DocumentType.Assessment);
When you have to shuffle data between sibling/nephew members, it gets a little more challenging.
To do this with AfterMap:
Mapper.CreateMap<SystemUser, DocumentVM>()
.AfterMap((src, dest) => dest.Document.DocumentsToReview =
src.Documents.Where(doc => doc.DocumentType == Enum.DocumentType.Assessment));

Categories