I created a controller for returning JSON to a mobile application. Given this action:
public JsonResult GetFirm(int id)
{
Firm firm = new Firm();
firm = dbContext.Firms.FirstOrDefault(s => s.id == id);
string firmString = JsonConvert.SerializeObject(firm);
return Json(firmString, JsonRequestBehavior.AllowGet);
}
This method(above method) throw self referencing loop error and I write :
Firm firm = new Firm();
firm = dbContext.Firms.FirstOrDefault(s => s.id == id);
string firmString = JsonConvert.SerializeObject(firm, Formatting.None,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
return Json(firmString, JsonRequestBehavior.AllowGet);
But this method send object with all child and collections and json include "\" before all attribute.
I want to send json object without child or collections.
Analysis and Issue
Your issue is clearly somewhere in your model you have a circular reference. I am highly confident it is due to you are trying to deserialize a database model (an EF model at a guess) directly. Doing that is not recommended.
A database model will contain references typically to other classes, which in themselves reference back to the first class due to representing a database structure. So you likely have something like:
public class ParentItemDataModel
{
[Key]
public Guid Id { get; set; }
public string SomeInfo { get; set; }
public List<ChildItemDataModel> Children { get; set; }
}
public class ChildItemDataModel
{
public ParentItemDataModel Parent { get; set; }
public string SomeInfo { get; set; }
}
So as you can see, a clear reference loop if deserialized.
Recommendation
Separate your API models returned to clients from your underlying database models by creating a new, simple class that contains only the required information such as:
public class ParentItemApiModel
{
public Guid Id { get; set; }
public string SomeInfo { get; set; }
}
Then you can get that information into the model the old-fashioned way:
var parentApiModel = new ParentItemApiModel
{
Id = dataModel.Id,
SomeInfo = dataModel.SomeInfo
};
Or you can do it with an extension method in your data model (so your typically lower core Api model has no reference back up to the database layer):
public static class DataModelExtensions
{
public static ParentItemApiModel ToApiModel(this ParentItemDataModel dataModel)
{
return new ParentItemApiModel
{
Id = dataModel.Id,
SomeInfo = dataModel.SomeInfo
};
}
}
So that now you can do this:
var apiModel = dataModel.ToApiModel();
Or in your example that would then be:
string firmString = JsonConvert.SerializeObject(firm.ToApiModel());
Or any other way you like.
Direct fix
If you do not want to fix it by the recommendation, the quickest, simplest way is to flag your data model Firm properties that you don't want in the JSON model with [JsonIgnore].
Try returing a string instead of JsonResult
public string GetFirm(int id)
{
Firm firm = new Firm();
firm = dbContext.Firms.FirstOrDefault(s => s.id == id);
string firmString = JsonConvert.SerializeObject(firm, Formatting.None,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
return firmString;
}
Related
We are using AutoMapper (9.0.0) in .net core for mapping values between source and destination. Till time this is working fine. However, we need to keep some of the values in destination as it is after mapping.
We have tried to used UseDestinationValue() and Ignore() methods on member, but it is not preserving the existing values. Below is the code for the same.
RequestModel
public class RequestModel
{
public int Id { get; set; }
public int SubmittedById { get; set; }
public string Description { get; set; }
public string Location { get; set; }
}
RequestDto
public class RequestDto
{
public int Id { get; set; }
public int SubmittedById { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public string SubmittedByName { get; set; }
}
We are accepting Dto in API as request parameter
API
[HttpPost]
public IActionResult Save([FromBody] RequestDto requestDto)
{
// Some logic to save records
}
So, before saving the records we are mapping RequestDto to RequestModel and passing that model to DAL layer to save the records like this
var requestModel = MapperManager.Mapper.Map<RequestDto, RequestModel>(RequestDto);
And call to data layer
var requestModel = DAL.Save(RequestModel)
So, after receiving the updated request model we are again mapping it to requestDto, in this case we are loosing the value for SubmittedByName property.
return MapperManager.Mapper.Map<RequestModel, RequestDto>(requestModel);
Mapper Class
public class RequestProfile: Profile
{
public RequestProfile()
{
CreateMap<RequestModel, RequestDto>()
CreateMap<RequestDto, RequestModel>()
}
}
This SubmittedByName column is not present in the Request table, but we want to utilize its value after saving the records.
So, how can we preserve the destination value after mapping.
Any help on this appreciated !
I think you have to use the Map overload that accepts destination.
This works for me, using same model / dto you posted, in a console application:
var config = new MapperConfiguration(cfg => cfg.CreateMap<RequestModel, RequestDto>().ReverseMap());
var mapper = config.CreateMapper();
var source = new RequestDto
{
Id = 1,
SubmittedById = 100,
SubmittedByName = "User 100",
Description = "Item 1",
Location = "Location 1"
};
Console.WriteLine($"Name (original): {source.SubmittedByName}");
var destination = mapper.Map<RequestDto, RequestModel>(source);
Console.WriteLine($"Name (intermediate): {source.SubmittedByName}");
source = mapper.Map<RequestModel, RequestDto>(destination, source);
Console.WriteLine($"Name (final): {source.SubmittedByName}");
The standard Map method creates a new object but the overloaded method uses existing object as destination.
We have tried to used UseDestinationValue() and Ignore() methods on member, but it is not preserving the existing values. Below is the code for the same.
since that didn't work for you
I would suggest creating a generic class like this (assuming you have multiple classes of RequestDto)
class RequesterInfo<T>
{
public string RequesterName { get; set; } // props you want to preserve
public T RequestDto { get; set; } // props to be mapped
}
by keeping the mapping as it is,
and modifying your code to something like this:
var requestModel = MapperManager.Mapper.Map<RequestDto, RequestModel>(RequesterInfo.RequestDto);
so what happens is that you modify the T RequestDto part of the object without modifying other properties.
I have two user objects, Student and Professor. Each user can have their own settings, which looks like this:
public class PersonSettings
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
public PersonSettingsValues Settings { get; set; }
}
public class PersonSettingsValues
{
public bool NotificationsEnabled { get; set; }
}
public class StudentSettingsValues : PersonSettingsValues
{
public int GymPassTypeId { get; set; }
}
public class ProfessorSettingsValues : PersonSettingsValues
{
public bool AllowOfficeHours { get; set; }
}
Some settings are shared, but Students and Professors also have their own settings, so I have inherited models.
I'm just storing the settings as json in the database by using conversion values on PersonSettings:
builder.Property(ps => ps.Settings).HasConversion(
s => JsonConvert.SerializeObject(s, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }),
s => JsonConvert.DeserializeObject<PersonSettingsValues>(s, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
So the PersonSettings table is populated with several records with settings, some have the data in the Settings column formatted as StudentSettings and others are ProfessorSettings
What I'd like to do is have an endpoint that will return StudentSettings for a Student. I have:
[HttpGet("{personId}")]
public async Task<IActionResult> GetStudentSettings([FromRoute] Guid personId)
{
var personSettings = await _context.PersonSettings
.FirstOrDefaultAsync(ps => ps.PersonId == id);
return Ok(personSettings);
}
The problem is that the Settings property is populated with only the PersonSettingsValues, I don't get the StudentSettingsValues with it. I'm not sure how to make it do that.
I tried using a DTO instead and mapping to it with AutoMapper:
public class StudentSettingsDTO
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
public StudentSettingsValues Settings { get; set; }
}
...
[HttpGet("{personId}")]
public async Task<IActionResult> GetStudentSettings([FromRoute] Guid personId)
{
var personSettings = await _context.PersonSettings
.FirstOrDefaultAsync(ps => ps.PersonId == id);
return Ok(_mapper.Map<StudentSettingsDTO>(personSettings));
}
...
CreateMap<PersonSettings, StudentSettingsDTO>()
.ForMember(ps => ps.Settings,
o => o.ResolveUsing(s => (StudentSettingsValues)s.Settings));
In my mapping, I'm just trying to cast the Settings as a StudentSettingsValues type, but I get an error saying
Unable to cast object of type 'PersonSettingsValues' to type 'StudentSettingsValues'
Not sure what I'm doing wrong here, how can I just return a PersonSettings object with either StudentSettings or ProfessorSettings on it?
It's because your builder, when parsing the json from database, parses to PersonSettingsValues. Once that's done, you lose all the other data. You have a few options:
Not sure what this builder exactly is and how much custom logic you
can add. But if at the time of deserialization you know if it's
professor or student, you should deserialize to object you need.
Then even if you assign it PersonSettingsValues you will still be
able to cast it later on to a more concrete type.
Instead of different models, make interfaces. IPersonSettingsValues,
IStudentSettingsValues, and IProfessorSettingsValues. And then have
just one model that implements all of the interfaces. Then your
deserializer can serialize to that one model. The values that don't
exist will go to default, but it will always be deserialized. And
then when you want to use it from specific context, you will be able
to cast it to a specific interface.
Deserialize to a dynamic object(thus it just deserializes whatever is in the json), and then use autoMapper to map dynamic object to a concrete type.
If it's not clear, tell me and I will try to come up with a code example.
Goal: to save ViewModel object by Entity Framework. I have UserViewModel object which has list of UnitViewModel. Then, I have a UserAdapter class which converts UserViewModel into Entity Framework User object (see Convert()below how).
Now, my question is how do I convert this list of UnitViewModel to its corresponding Entity Framework Unit list? - Do I have to get each object from DB Context by calling something like context.Units.Where(u=>myListofUnitIDs.Contains(u.UnitID))?
public class UserViewModel
{
public Guid? UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? CreateTime { get; set; }
public List<UnitViewModel> UserUnits { get; set; }
}
public class UnitViewModel
{
public Guid UnitID { get; set; }
public string Name { get; set; }
public int? SortIndex { get; set; }
public DateTime CreateTime { get; set; }
public bool Assigned { get; set; }
}
public class UserAdapter
{
public static User Convert(UserViewModel userView)
{
User user;
if (userView.UserID.HasValue)
{
using (var provider = new CoinsDB.UsersProvider())
{
user = provider.GetUser(userView.UserID.Value);
}
}
else
{
user = new User();
}
user.FirstName = userView.FirstName;
user.LastName = user.LastName;
user.Password = StringHelper.GetSHA1(userView.Password);
user.UserName = user.UserName;
user.CreateTime = DateTime.Now;
// Problem here :)
// user.Units = userView.UserUnits;
return user;
}
}
UPDATE: The main concern here is that I have to retrieve each Unit from database to match (or map) it with ViewModel.Unit objects, right? Can I avoid it?
For your information, this operation is called as Mapping mainly. So, you want to map your view model object to the entity object.
For this, you can either use already existed 3rd party library as AutoMapper. It will map properties by reflection which have same name. Also you can add your custom logic with After method. But, this approach has some advantages and disadvantages. Being aware of these disadvantages could help you to decide whether you must use this API or not. So, I suggest you to read some articles about advantages and disadvantages of AutoMapper especially for converting entities to other models. One of such disadvantages is that it can be problem to change the name of one property in the view model in the future, and AutoMapper will not handle this anymore and you won't get any warning about this.
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit = Mapper.Map<UnitViewModel, UserUnit>(item);
user.Units.Add(userUnit);
}
So, I recommend to write your custom mappers.
For example, I have created a custom library for this and it maps objects lik this:
user.Units = userView.UserUnits
.Select(userUnitViewModel => userUnitViewModel.MapTo<UserUnit>())
.ToList();
And I am implementing these mapping functions as:
public class UserUnitMapper:
IMapToNew<UnitViewModel, UserUnit>
{
public UnitViewModel Map(UserUnit source)
{
return new UnitViewModel
{
Name = source.Name,
...
};
}
}
And then in runtime, I am detecting the types of the objects which will be used during mapping, and then call the Map method. In this way, your mappers will be seperated from your action methods. But, if you want it urgently, of course you can use this:
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit= new UserUnit()
{
Name = item.Name,
...
};
user.Units.Add(userUnit);
}
I have a similar structure to the one below
Base class
public class BaseClass
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public Guid Guid { get; set; }
public string Hometown { get; set; }
}
Derived Class
public class DerivedClass : BaseClass
{
public List<DerivedClassDataItem> Data { get; set; }
}
Data class
public class DerivedClassDataItem
{
public string Datum1 { get; set; }
public string Datum2 { get; set; }
public string Datum3 { get; set; }
public string Datum4 { get; set; }
public int Datum5 { get; set; }
public DateTime Datum6 { get; set; }
}
What is the best practice to return specific set of info from the DerivedClass?
a potential set could be:
Name, Address, Guid and then a Data list that only contains Datum1 and Datum4
I could see anonymousTypes, Tuples or another set of class(es), all to be valid approaches.
My concern about creating new set of classs for the set returned is that the class(s) structure will be similar to the structure of the three mentioned above except it will have fewer selected members, which to me, does not sound ideal. (duplicate code and structure)
Using anonymousTypes was my initial solution to tackle this, something like
List<DerivedClass> list = new List<DerivedClass>();
var mySet = list.Select(d => new
{
Name = d.Name,
Address = d.Address,
.
.
.
.
.
Data = d.Data.Select(item => new
{
Datum1 = item.Datum1,
Datum4 = item.Datum4
})
});
but again, that was a headache for us to track through httpResponse and through out API calls.
Should I go with Tuple?
Any insights as to what is the best practice for doing this?
Edit
I am using this set of data to be a response returned by a API/GET call. I will send the set back using HttpRespose and then the framework will transform that into json
this is an actual method we have now
private void populateReturnFile()
{
var returnFileAnonymous = new
{
Vendor = this.Vendor,
OrganizationName = this.OrganizationName,
User = this.User,
Platform = this.Platform,
DictionaryType = this.DictionaryType,
UseCaseId = this.UseCaseId,
Data = this.Data.Select(d => new
{
MigrationTermId = d.MigrationTermId,
ImoLexicalCode = d.ImoLexicalCode
})
};
this.returnFile = returnFileAnonymous;
}
Then my GET will return the retunFile (this is a very simple method, i have remove irrelevant code)
[HttpGet]
public HttpResponseMessage Get(Guid migrationFileId)
{
ProblemList problemList = ProblemList.GetProblemList(migrationFileId);
return Request.CreateResponse(HttpStatusCode.OK, problemList.ReturnFile, new JsonMediaTypeFormatter());
}
If API calls is where you are using these classes, then I personally like to keep it simple and avoid complex inheritance hierarchy. Remember, simple code is good code.
I would make a separate class for each api request/response call. For very simple api calls (ajax requests for example) I like to use anonymous types, but for controllers that only handle API calls I like to create separate classes, organized in a nice folder structure.
Everyone has their "style" but as long as you strive for simplicity your code will be maintainable.
I've looked and tried every single solution posted here, with no avail.
My problem is:
On a web solution (ASP.NET MVC 3 C# / Razor), I'm using Json.Net to serialize the data displayed on some reports, to be able to send it over to a WPF application.
These reports results are a collection of Model objects.
I have the same Model objects on the WPF application, so when I deserialize the Json string, I would like to bind the results accordingly (keeping the original Model object).
The Assembly name and Object type are different on each end (Web / App) - different namespaces.
Here's what I've tried so far:
On the web solution:
// MyModel
public class MyModel
{
public long Id { get; set; }
public string Name { get; set; }
}
...
// data = IEnumerable<MyModel>
var jsonData = JsonConvert.SerializeObject(data.ToArray(), data.ToArray().GetType(),
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
On the app:
// MyModel
public class MyModel
{
[JsonProperty("Id")]
public long Id { get; set; }
[JsonProperty("Name")]
public string Name { get; set; }
}
...
var jsonArray = JsonConvert.DeserializeObject(e.jsonObject,
null,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
Binder = new MySerializationBinder()
});
...
public class MySerializationBinder : DefaultSerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
return typeof(MyModel);
}
}
Can anyone give me a hand on this, please?
Thanks!
UPDATE
As per #Marc Gravell comment:
I forgot to mention the main issue here. I need to send the Object type across to the WPF app, because the listener will be expecting data from many reports - which are collections of different Models. So, when binding it back, I know which Object should be binded.
I stand by my original answer - type information in serialization data is just really messy - it would be far better to change the code not to need this, but to "fix" it (not sure that is the right word) - look carefully at the typeName - it isn't always what you are expecting:
public class MySerializationBinder : DefaultSerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
switch(typeName)
{
case "WebSolution.MyModel[]": return typeof(Application.MyModel[]);
case "WebSolution.MyModel": return typeof(Application.MyModel);
default: return base.BindToType(assemblyName, typeName);
}
}
}
Incidentally, once the array type is known, the element type is implicit - so you can save some effort by only including the array type:
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Arrays
}
But I still advise: don't do it this way.
The "mistake" here is including the full type names in the first place; the presence of type names in json should usually be the warning sign of a code-smell. Remove those, and there is nothing to do - it just works:
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
static class Program
{
static void Main()
{
// here imagine we're in the web tier, serializing
var data = GetData();
var jsonData = JsonConvert.SerializeObject(
data.ToArray(), Formatting.None);
// now imagine we're at the application, deserializing
var appData = JsonConvert.DeserializeObject<Application.MyModel[]>(
jsonData);
// and it all works fine
}
static IEnumerable<WebSolution.MyModel> GetData()
{
yield return new WebSolution.MyModel { Id = 123, Name = "abc" };
yield return new WebSolution.MyModel { Id = 456, Name = "def" };
}
}
namespace WebSolution
{
// MyModel
public class MyModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
namespace Application
{
// MyModel
public class MyModel
{
[JsonProperty("Id")]
public long Id { get; set; }
[JsonProperty("Name")]
public string Name { get; set; }
}
}