I am trying to write some logic to get entity values from the database based on a dynamic table value. Following this post has gotten me to the point of TEntity Entity framework - get entity by name
I not following what is being said here. The following code:
public static void PublishTable(string user, ContentFields tableToPublish)
{
var jsonData = DataAccess.GetEnvironmentJson((int)Environments.Production);
var model = JsonConvert.DeserializeObject<Models.FullDataSetModel>(jsonData);
using (var db = new LoginPageContentEntities())
{
var deployEntry = db.Deployment.Find((int)Environments.Production);
deployEntry.DateUpdated = DateTime.Now;
var pendingChangesFlag = deployEntry.GetType().GetProperty(tableToPublish.ToString());
pendingChangesFlag.SetValue(deployEntry, false);
var publishToModel = model.GetType().GetProperty(tableToPublish.ToString());
var tableValue = (IEnumerable<????>)typeof(LoginPageContentEntities).GetProperty(tableToPublish.ToString()).GetValue(db, null));
publishToModel.SetValue(model, tableValue.ToList());
var json = JsonConvert.SerializeObject(model);
deployEntry.JsonCache = json;
db.SaveChanges();
}
}
For example if I pass the entity of ProductInformation in IEnumerable<ProductInformation> this works when I alter that specific entity.
Runs up to the section of the cast, I do not follow what needs to be done to get the values from the context. They are defining TEntity but there is nothing in the definition of it.
Your question seems to be - What is TEntity?
TEntity is generic. From the link provided in the comment https://msdn.microsoft.com/en-us/library/ms379564%28v=vs.80%29.aspx#csharp_generics_topic2
See the code block
public class Stack<T>
{
T[] m_Items;
public void Push(T item)
{...}
public T Pop()
{...}
}
T is specifying generic. You can plugin class - like the one you have ProductInformation while instantiating Stack. For example -
var stk = new Stack<ProductInformation>();
I managed to solve my issue with some help from the other answers, however i'm posting my solution as it applied to working with EF.
public static void PublishTable<TEntity>()
{
var targetEntity = typeof(TEntity).Name;
var model = JsonConvert.DeserializeObject<Models.FullDataSetModel>(DataAccess.GetEnvironmentJson((int)Environments.Production));
using (var db = new LoginPageContentEntities())
{
var deployEntry = db.Deployment.Find((int)Environments.Production);
deployEntry.DateUpdated = DateTime.Now;
deployEntry.GetType().GetProperty(targetEntity).SetValue(deployEntry, false);
model.GetType().GetProperty(targetEntity).SetValue(model, ((IEnumerable<TEntity>)(typeof(LoginPageContentEntities).GetProperty(targetEntity).GetValue(db, null))).ToList());
deployEntry.JsonCache = JsonConvert.SerializeObject(model);
db.SaveChanges();
}
}
Related
I am new with c# controllers and I am trying to join two entities with a LINQ query. But I am getting a 'missing a cast' error message as shown below that I don't understand and it does not help me to correct the error.
The controller looks like:
public class SoonDueReportsController_simple : BaseODataController
{
public SoonDueReportsController_simple(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public SoonDueReportsController_simple Get()
{
var falligkeiten = UnitOfWork.GetAll<Fälligkeiten>();
var betriebe = UnitOfWork.GetAll<Betriebe>();
var query = (from betrieb in betriebe.AsQueryable()
join fallig in falligkeiten.AsQueryable()
on betrieb.ID equals
fallig.Betrieb_ID
where fallig.Fälligkeit_Erledigt_Datum == null
&& betrieb.Aktiv == true
select new
{
BetriebId = betrieb.ID,
FalligkeitObject = fallig.Fälligkeit_Objekt
});
return query;
}
}
This type of controller I have used with success for single entities (tables from an sql db) to display static data in a kendo grid. But I fail when I try to join two tables as shown above. If someone could help me with this problem I'd appreciate it very much.
Regards, Manu
You select a collection of anonymous objects
select new
{
BetriebId = betrieb.ID,
FalligkeitObject = fallig.Fälligkeit_Objekt
});
And want the method to return a instance of certain type. C# is the strongly typed language without type inference, which means you have to specifically create objects of a certain type or interface if you want to return them.
Also, you are have the controller type itself to be returned from the Get method. This makes no sense. I actually do not know what you want to do but may be this would work:
public class SoonDueReportsController_simple : BaseODataController
{
public SoonDueReportsController_simple(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<SomeModel> Get()
{
var falligkeiten = UnitOfWork.GetAll<Fälligkeiten>();
var betriebe = UnitOfWork.GetAll<Betriebe>();
var query = (from betrieb in betriebe.AsQueryable()
join fallig in falligkeiten.AsQueryable()
on betrieb.ID equals
fallig.Betrieb_ID
where fallig.Fälligkeit_Erledigt_Datum == null
&& betrieb.Aktiv == true
select new SomeModel
{
BetriebId = betrieb.ID,
FalligkeitObject = fallig.Fälligkeit_Objekt
});
return query;
}
}
public class SomeModel
{
public int BetriebId { get; set; }
public string FalligkeitObject { get; set; }
}
Please bear in mind that there are no such things as "C# controllers". You are working with OData, so I would recommend you to look at some OData resources, there are plenty of examples out there.
And one last thing, don't get me wrong, but it does not help giving properties, types and other identifiers German names. People would have hard time trying to understand your code.
The exception explains to you the problem exactly. You're wanting to return a type of 'SoonDueReportsController_simple' and yet you are returning a Queryable where a' is your new { ..., ... } object.
I like the suggestion given to make a strong typed object and fill it but you can also return a dynamic type.
This code works to explain:
private dynamic Get() => new { Name = "SomeName", Age = 31 };
private void TestGet()
{
var obj = Get();
var name = obj.Name;
var age = obj.Age;
}
I have a complex object that I've broken down to a ViewModel for form. I'll illustrate a sample of the structure but the number of fields are around 60 total from about 6 different entities intermingled.
//ViewModel
public class SomeViewModel
{
public TabOne TabOne {get;set;}
public TabTwo TabTwo {get;set;}
public SomeViewModel(ComplexObject co)
{
this.TabOne = new TabOne { Name = co.Name, Value = co.Value};
this.TabTwo = new TabTwo { Name = co.Name, Another = co.Another };
}
}
All this works fine and I get to the Controller from the View
//Controller
[HttpPost]
public ActionResult Update(SomeViewModel vm)
{
//TODO: 1. Know which properties in the ViewModel changed for auditing
//TODO: 2. Update all changed EF entities in the database.
}
Before sending the ViewModel to the View I cache the original Value in a Session object and use reflection to compare the two. It's drawn out and I'll have to do a ton of work for any changes. Is there a better way??
The ViewModel is different structure from the EF Models, so it's a bunch of custom mapping that will need to be done to Update the correct entities. I'd like to avoid that because of the number of fields. Is there a better way?
Ok, not sure if this is what you need but for what I understood:
You can get the original and current values like this:
using (var dbCtx = new YourDBEntities())
{
var yourEntity = dbCtx.YourEntity.Find(1);
var entry = dbCtx.Entry(yourEntity);
foreach (var propertyName in entry.CurrentValues.PropertyNames )
{
Console.WriteLine("Property Name: {0}", propertyName);
var originalVal = entry.OriginalValues[propertyName];
Console.WriteLine("Original Value: {0}", originalVal);
var currentVal = entry.CurrentValues[propertyName];
Console.WriteLine("Current Value: {0}", currentVal);
}
}
For mapping ViewModels to your EF Models you can use Automapper, it can be as easy as:
AutoMapper.Mapper.CreateMap<Book, BookViewModel>();
var model = AutoMapper.Mapper.Map<BookViewModel>(book);
Edit:
Ok, if you need to do it manually, you can use the following method for mapping objects:
public static void MapObjects( object source, object destiny)
{
var modelPropertiesName = new HashSet<string>(source.GetType().GetProperties().Select(x => x.Name));
var entityProperties = destiny.GetType().GetProperties();
var propertyList = entityProperties.Where(p => modelPropertiesName.Contains(p.Name))
.ToList();
foreach (var prop in propertyList)
{
var modelProperty = source.GetType().GetProperty(prop.Name);
var value = modelProperty.GetValue(source);
prop.SetValue(destiny, value, null);
}
}
You just need to pass a destiny object and a source from where to map the properties.
I am trying to update a specific part with following code:
var nationalPart = _contentManager.Get<NationalPart>(Id);
nationalPart.Name = part.Name;
i have already done this.but now it is not working.even i call contentManager.Flush() explicitly, but still no luck. can anyone help me with this?
thanks in advance.
EDIT: i found where my problem originates from!
when i make a JSON request to update a contentPart.the in memory version is updating but it is not reflecting the result to db.now we are one step closer to answer.but what is preventing the session from updating the db?
EDIT2:
this is code for my handler :
public NationalPartHandler(IRepository<NationalPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
this is code for Controller Action:
[GridAction(EnableCustomBinding = true)]
public JsonResult UpdateNational(NationalViewModel Model, GridCommand Command)
{
if (!ModelState.IsValid)
return new JsonResult { Data = "error" };
var NationalPart = _contentmanager.Get(Model.Id).As<NationalPart>();
NationalPart.Record = new NationalPartRecord();
NationalPart.Record.MapFrom<NationalPartRecord, NationalViewModel>(Model);
_soccerService.UpdateNational(NationalPart);
return NationalsListJson(Command);
}
and finally code for service:
public void UpdateNational(NationalPart part)
{
var nationalPart = _contentManager.Get<NationalPart>(part.Id);
nationalPart.Name = part.Name;
_contentManager.Flush();
}
Remove the line
NationalPart.Record = new NationalPartRecord();
The underlying record is automatically generated and is proxied so NHibernate can save the data to the database whenever you do any change to it. You don't want to replace that ever, otherwise no data will get saved.
UpdateNational is redundant and you can remove it - you already have part.Name (which if I understand correctly, should map to part.Record.Name) set during call to record.MapFrom.
So the correct version would look like this
[GridAction(EnableCustomBinding = true)]
public JsonResult UpdateNational(NationalViewModel Model, GridCommand Command)
{
if (!ModelState.IsValid)
return new JsonResult { Data = "error" };
var part = _contentmanager.Get(Model.Id).As<NationalPart>();
part.Record.MapFrom<NationalPartRecord, NationalViewModel>(Model);
return NationalsListJson(Command);
}
given your NationalPart looks similar to:
public NationalPart : ContentPart<NationalPartRecord>
{
public string Name {
get { return Record.Name; }
set { Record.Name = value; }
}
}
Summarizing - if you want to store some data in Orchard, all you need to do is to set a value of some property of a record, nothing more. Data will get persisted at the end of a request (or earlier, if you call IContentManager.Flush()).
If I have a controller like this:
[HttpPost]
public JsonResult FindStuff(string query)
{
var results = _repo.GetStuff(query);
var jsonResult = results.Select(x => new
{
id = x.Id,
name = x.Foo,
type = x.Bar
}).ToList();
return Json(jsonResult);
}
Basically, I grab stuff from my repository, then project it into a List<T> of anonymous types.
How can I unit-test it?
System.Web.Mvc.JsonResult has a property called Data, but it's of type object, as we expected.
So does that mean if I want to test that the JSON object has the properties I expect ("id", "name", "type"), I have to use reflection?
EDIT:
Here's my test:
// Arrange.
const string autoCompleteQuery = "soho";
// Act.
var actionResult = _controller.FindLocations(autoCompleteQuery);
// Assert.
Assert.IsNotNull(actionResult, "No ActionResult returned from action method.");
dynamic jsonCollection = actionResult.Data;
foreach (dynamic json in jsonCollection)
{
Assert.IsNotNull(json.id,
"JSON record does not contain \"id\" required property.");
Assert.IsNotNull(json.name,
"JSON record does not contain \"name\" required property.");
Assert.IsNotNull(json.type,
"JSON record does not contain \"type\" required property.");
}
But I get a runtime error in the loop, stating "object does not contain a definition for id".
When I breakpoint, actionResult.Data is defined as a List<T> of anonymous types, so I figure if I enumerate through these, I can check the properties. Inside the loop, the object does have a property called "id" - so not sure what the issue is.
I know I'm a bit late on this guys, but I found out why the dynamic solution wasn't working:
JsonResult returns an anonymous object and these are, by default, internal, so they need to be made visible to the tests project.
Open your ASP.NET MVC application project and find AssemblyInfo.cs from folder called Properties. Open AssemblyInfo.cs and add the following line to the end of this file.
[assembly: InternalsVisibleTo("MyProject.Tests")]
Quoted from: http://weblogs.asp.net/gunnarpeipman/archive/2010/07/24/asp-net-mvc-using-dynamic-type-to-test-controller-actions-returning-jsonresult.aspx
I thought it would be nice to have this one for the record. Works like a charm
RPM, you look to be correct. I still have much to learn about dynamic and I cannot get Marc's approach to work either. So here is how I was doing it before. You may find it helpful. I just wrote a simple extension method:
public static object GetReflectedProperty(this object obj, string propertyName)
{
obj.ThrowIfNull("obj");
propertyName.ThrowIfNull("propertyName");
PropertyInfo property = obj.GetType().GetProperty(propertyName);
if (property == null)
{
return null;
}
return property.GetValue(obj, null);
}
Then I just use that to do assertions on my Json data:
JsonResult result = controller.MyAction(...);
...
Assert.That(result.Data, Is.Not.Null, "There should be some data for the JsonResult");
Assert.That(result.Data.GetReflectedProperty("page"), Is.EqualTo(page));
I'm a bit late to the party, but I created a little wrapper that lets me then use dynamic properties. As of this answer I've got this working on ASP.NET Core 1.0 RC2, but I believe if you replace resultObject.Value with resultObject.Data it should work for non-core versions.
public class JsonResultDynamicWrapper : DynamicObject
{
private readonly object _resultObject;
public JsonResultDynamicWrapper([NotNull] JsonResult resultObject)
{
if (resultObject == null) throw new ArgumentNullException(nameof(resultObject));
_resultObject = resultObject.Value;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (string.IsNullOrEmpty(binder.Name))
{
result = null;
return false;
}
PropertyInfo property = _resultObject.GetType().GetProperty(binder.Name);
if (property == null)
{
result = null;
return false;
}
result = property.GetValue(_resultObject, null);
return true;
}
}
Usage, assuming the following controller:
public class FooController : Controller
{
public IActionResult Get()
{
return Json(new {Bar = "Bar", Baz = "Baz"});
}
}
The test (xUnit):
// Arrange
var controller = new FoosController();
// Act
var result = await controller.Get();
// Assert
var resultObject = Assert.IsType<JsonResult>(result);
dynamic resultData = new JsonResultDynamicWrapper(resultObject);
Assert.Equal("Bar", resultData.Bar);
Assert.Equal("Baz", resultData.Baz);
Here's one I use, perhaps it is of use to anyone. It tests an action that returns a JSON object for use in clientside functionality. It uses Moq and FluentAssertions.
[TestMethod]
public void GetActivationcode_Should_Return_JSON_With_Filled_Model()
{
// Arrange...
ActivatiecodeController activatiecodeController = this.ActivatiecodeControllerFactory();
CodeModel model = new CodeModel { Activation = "XYZZY", Lifespan = 10000 };
this.deviceActivatieModelBuilder.Setup(x => x.GenereerNieuweActivatiecode()).Returns(model);
// Act...
var result = activatiecodeController.GetActivationcode() as JsonResult;
// Assert...
((CodeModel)result.Data).Activation.Should().Be("XYZZY");
((CodeModel)result.Data).Lifespan.Should().Be(10000);
}
I extend the solution from Matt Greer and come up with this small extension:
public static JsonResult IsJson(this ActionResult result)
{
Assert.IsInstanceOf<JsonResult>(result);
return (JsonResult) result;
}
public static JsonResult WithModel(this JsonResult result, object model)
{
var props = model.GetType().GetProperties();
foreach (var prop in props)
{
var mv = model.GetReflectedProperty(prop.Name);
var expected = result.Data.GetReflectedProperty(prop.Name);
Assert.AreEqual(expected, mv);
}
return result;
}
And i just run the unittest as this:
- Set the expected data result:
var expected = new
{
Success = false,
Message = "Name is required"
};
- Assert the result:
// Assert
result.IsJson().WithModel(expected);
My solution is to write the extension method:
using System.Reflection;
using System.Web.Mvc;
namespace Tests.Extensions
{
public static class JsonExtensions
{
public static object GetPropertyValue(this JsonResult json, string propertyName)
{
return json.Data.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public).GetValue(json.Data, null);
}
}
}
If in the test you know what exactly the Json data result should be then you can just do something like this:
result.Data.ToString().Should().Be(new { param = value}.ToString());
P.S. This would be if you had used FluentAssertions.Mvc5 - but it shouldn't be hard to convert it to whatever testing tools you use.
This is how I assert it
foreach (var item in jsonResult.Data as dynamic) {
((int)item.Id).ShouldBe( expected Id value );
((string)item.name).ShouldBe( "expected name value" );
}
You can convert the value property of the JsonResult into an instance of a known type and then simply access it's properties.
public static class JsonResultExtensions
{
public static T ExtractType<T>(this JsonResult result)
{
var resultAsJson = JsonSerializer.Serialize(result.Value);
return JsonSerializer.Deserialize<T>(resultAsJson);
}
}
below is an example of the use of the extension method:
MyModel model = jsonResult.ExtractType<MyModel>();
Assert.True(model.Success);
I am trying to do a simple JSON return but I am having issues I have the following below.
public JsonResult GetEventData()
{
var data = Event.Find(x => x.ID != 0);
return Json(data);
}
I get a HTTP 500 with the exception as shown in the title of this question. I also tried
var data = Event.All().ToList()
That gave the same problem.
Is this a bug or my implementation?
It seems that there are circular references in your object hierarchy which is not supported by the JSON serializer. Do you need all the columns? You could pick up only the properties you need in the view:
return Json(new
{
PropertyINeed1 = data.PropertyINeed1,
PropertyINeed2 = data.PropertyINeed2
});
This will make your JSON object lighter and easier to understand. If you have many properties, AutoMapper could be used to automatically map between DTO objects and View objects.
I had the same problem and solved by using Newtonsoft.Json;
var list = JsonConvert.SerializeObject(model,
Formatting.None,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});
return Content(list, "application/json");
This actually happens because the complex objects are what makes the resulting json object fails.
And it fails because when the object is mapped it maps the children, which maps their parents, making a circular reference to occur. Json would take infinite time to serialize it, so it prevents the problem with the exception.
Entity Framework mapping also produces the same behavior, and the solution is to discard all unwanted properties.
Just expliciting the final answer, the whole code would be:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
new {
Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
}
, JsonRequestBehavior.AllowGet
);
}
It could also be the following in case you don't want the objects inside a Result property:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
(from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
, JsonRequestBehavior.AllowGet
);
}
To sum things up, there are 4 solutions to this:
Solution 1: turn off ProxyCreation for the DBContext and restore it in the end.
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
bool proxyCreation = db.Configuration.ProxyCreationEnabled;
try
{
//set ProxyCreation to false
db.Configuration.ProxyCreationEnabled = false;
var data = db.Products.ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
finally
{
//restore ProxyCreation to its original state
db.Configuration.ProxyCreationEnabled = proxyCreation;
}
}
Solution 2: Using JsonConvert by Setting ReferenceLoopHandling to ignore on the serializer settings.
//using using Newtonsoft.Json;
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.ToList();
JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);
return Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
Following two solutions are the same, but using a model is better because it's strong typed.
Solution 3: return a Model which includes the needed properties only.
private DBEntities db = new DBEntities();//dbcontext
public class ProductModel
{
public int Product_ID { get; set;}
public string Product_Name { get; set;}
public double Product_Price { get; set;}
}
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new ProductModel
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
Solution 4: return a new dynamic object which includes the needed properties only.
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
JSON, like xml and various other formats, is a tree-based serialization format. It won't love you if you have circular references in your objects, as the "tree" would be:
root B => child A => parent B => child A => parent B => ...
There are often ways of disabling navigation along a certain path; for example, with XmlSerializer you might mark the parent property as XmlIgnore. I don't know if this is possible with the json serializer in question, nor whether DatabaseColumn has suitable markers (very unlikely, as it would need to reference every serialization API)
add [JsonIgnore] to virtuals properties in your model.
Using Newtonsoft.Json: In your Global.asax Application_Start method add this line:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Its because of the new DbContext T4 template that is used for generating the EntityFramework entities. In order to be able to perform the change tracking, this templates uses the Proxy pattern, by wrapping your nice POCOs with them. This then causes the issues when serializing with the JavaScriptSerializer.
So then the 2 solutions are:
Either you just serialize and return the properties you need on the client
You may switch off the automatic generation of proxies by setting it on the context's configuration
context.Configuration.ProxyCreationEnabled = false;
Very well explained in the below article.
http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/
Provided answers are good, but I think they can be improved by adding an "architectural" perspective.
Investigation
MVC's Controller.Json function is doing the job, but it is very poor at providing a relevant error in this case. By using Newtonsoft.Json.JsonConvert.SerializeObject, the error specifies exactly what is the property that is triggering the circular reference. This is particularly useful when serializing more complex object hierarchies.
Proper architecture
One should never try to serialize data models (e.g. EF models), as ORM's navigation properties is the road to perdition when it comes to serialization. Data flow should be the following:
Database -> data models -> service models -> JSON string
Service models can be obtained from data models using auto mappers (e.g. Automapper). While this does not guarantee lack of circular references, proper design should do it: service models should contain exactly what the service consumer requires (i.e. the properties).
In those rare cases, when the client requests a hierarchy involving the same object type on different levels, the service can create a linear structure with parent->child relationship (using just identifiers, not references).
Modern applications tend to avoid loading complex data structures at once and service models should be slim. E.g.:
access an event - only header data (identifier, name, date etc.) is loaded -> service model (JSON) containing only header data
managed attendees list - access a popup and lazy load the list -> service model (JSON) containing only the list of attendees
Avoid converting the table object directly. If relations are set between other tables, it might throw this error.
Rather, you can create a model class, assign values to the class object and then serialize it.
I'm Using the fix, Because Using Knockout in MVC5 views.
On action
return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));
function
public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
{
TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
foreach (var item in Entity.GetType().GetProperties())
{
if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
item.SetValue(Entity_, Entity.GetPropValue(item.Name));
}
return Entity_;
}
You can notice the properties that cause the circular reference. Then you can do something like:
private Object DeCircular(Object object)
{
// Set properties that cause the circular reference to null
return object
}
//first: Create a class as your view model
public class EventViewModel
{
public int Id{get;set}
public string Property1{get;set;}
public string Property2{get;set;}
}
//then from your method
[HttpGet]
public async Task<ActionResult> GetEvent()
{
var events = await db.Event.Find(x => x.ID != 0);
List<EventViewModel> model = events.Select(event => new EventViewModel(){
Id = event.Id,
Property1 = event.Property1,
Property1 = event.Property2
}).ToList();
return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}
An easier alternative to solve this problem is to return an string, and format that string to json with JavaScriptSerializer.
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
It is important the "Select" part, which choose the properties you want in your view. Some object have a reference for the parent. If you do not choose the attributes, the circular reference may appear, if you just take the tables as a whole.
Do not do this:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.toList();
return j.Serialize(entityList );
}
Do this instead if you don't want the whole table:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
This helps render a view with less data, just with the attributes you need, and makes your web run faster.