Serialize nHibernate query to JSON - c#

In delving into Fluent nHibernate, I discovered a potential breaker for using it...
Given the following POCO code.
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Details Details { get; set; }
}
public class Details
{
public virtual int Id { get; set; }
public virtual IList<Orders> Orders { get; set; }
}
public class CustomerMap : ClassMap<Customer>
{
// perform mapping
}
public class DetailsMap : ClassMap<Details>
{
// perform mapping
}
I loaded up ASP.NET MVC and attempted to use Json Serialization.
using System.Web.Script.Serialization;
public static partial class JsonExtensions
{
public static string ToJson(this object item)
{
return new JavaScriptSerializer().Serialize(item);
}
}
And Lo, when I passed a query from my nHibernate context to the ToJson method, I got an error!
A circular reference was detected while serializing an object of type 'System.Reflection.RuntimeModule'.
It seems to do this whether I pull a single object, or a list of objects ..or anything for that matter. I've even tried marking my classes as [Serializable] with the same result. This doesn't happen with the exact same classes using the Microsoft Entity Framework Code-Only approach.
Can I not deserialize nHibernate DTOs into JSON?
Adding more code for examination.
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var customers= session.CreateCriteria(typeof(Customer)).List<Customer>();
foreach (var customer in customers)
{
Console.WriteLine(customer.ToJson());
}
Console.ReadLine();
}
}

This is just a hunch but you might like to investigate what type it is really trying to serialize- nHibernate generates proxies for each POCO at runtime (so it can do stuff like lazy loading of foreign key entities, etc.). This may be why you are getting this error.
Try specifying the exact type to be serialized or perhaps create a brand new object to serialize, populating its properties with that of the nHibernate POCO.
EDIT:
This seems to be much more likley the answer to your problems:
http://www.west-wind.com/WebLog/posts/147218.aspx
Basically, check all of your POCO's for any circular references (e.g. a Customer POCO that has an Order POCO as a property while the Order POCO has a list of Customer's as a property)

Related

EF Core DbDataReader result changing in interceptor

guys.
I have a question about EF Core DbCommandInterceptor.
Let's have a class with 2 fields like this
public class User
{
public Guid Id { get; set; }
public string SameData { get; set; }
}
public class TestClass
{
public Guid Id { get; set; }
public Guid TestClassId { get; set; }
[NotMapperd, MyAttr]
public TestClass TestClass { get; set; }
}
where User and TestClass are both located in the different contexts (for example, UserDbContext, TestDbContext). MyAttr is the marker attribute, nothing more.
So, I want to write an interceptor that raises up each time we try to get info about TestClasses, but after data got it should get an additional data about User with cross-request to UserDbContext (It possible, because I have User Id after the command execution and can use this Id in the request)
I know, that it should be DbCommandInterceptor.ReaderExecuted or DbCommandInterceptor.ReaderExecutedAsync in this case, but I cannot understand how to get information about objects in the result (I can get rows but I cannot understand what should I do, how should I map it). I can use additional libraries in the project if needed (like Dapper and others).
Could anyone helps me to get
Result Type - concrete entity type or entity collection type?
Result as a C# object (POCO or POCO collection)?
Thank you.
I know, that it should be DbCommandInterceptor.ReaderExecuted or DbCommandInterceptor.ReaderExecutedAsync in this case, but I cannot understand how to get information about objects in the result
The EF Command interceptors don't support that. You can replace the DataReader with a different one. But the query was generated to fill a particular object graph, which is not exposed by the interceptor API.

"The owned entity type requires to be referenced from another entity type via a navigation"

I have an entity in my application called Person. There are two types of users, Student and Professor, that inherit from Person.
Every Person has a settings property:
public abstract class Person
{
public Guid UserId { get; set; }
public string Name { get; set; }
public PersonSettings Settings { get; set; }
}
public class Student : Person
{
}
public class Professor : Person
{
}
My PersonSettings class is just a couple of properties. It isn't an entity to be stored in the database, so I marked it as Owned:
[Owned]
public class PersonSettings
{
public bool NotificationsEnabled { get; set; }
public int GymPassId { get; set; }
}
These are stored in the database as json, which I'm using EF Core conversion values in my Person entity configuration to serialize and deserialize it:
builder.Property(p => p.Settings).HasConversion(
s => JsonConvert.SerializeObject(s, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }),
s => JsonConvert.DeserializeObject<PersonSettings>(s, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
But when I try to run my application and do a database migration, I'm getting an error saying
The owned entity type 'PersonSettings' requires to be referenced from another entity type via a navigation. Add a navigation to an entity type that points at 'PersonSettings'.
What am I supposed to do here? I couldn't find anything on the error message. Not sure if it has anything to do with Person being an abstract class.
I also can't repro, but you don't need an Owned Type here.
Using Owned Types is an alternative to using JSON serialization of a non-scalar property. When using Owned Types the type is stored along with the Entity that references it. So as Owned Type EF would create the Person table with separate columns for Settings_NotificationEnabled, and Settings_GymPassId.
So you can simply remove the OwnedAttribute, and ensure that you don't declare it to be an Entity with a property of type DbSet<PersonSettings> in your DbContext.
As to which to pick, I would generally use an Owned Type for this scenario, so you could query the database by the individual PersonSettings properties.
Using JSON conversion of a non-scalar property is useful for the case where you have a collection, because EF Core does not currently support collections of Owned Types.
I had the same problem and what solved it for me was to follow the documentation here.
Basically what you want to do is add an OwnsOne in your entity configuration for Person:
builder.OwnsOne(p => p.PersonSettings, ps => {
//if need be add additional settings here for NotificationsEnabled and GymPassId
});
That will create a "link" between Person and PersonSettings which the migration generator uses to construct the script.
Same error here, and the issue was that I had a reference in the owned class to an entity:
public class OwnedClass // owned, NOT a DbSet
{
// ... other properties
public Guid? CalendarId { get; set; }
[ForeignKey(nameof(CalendarId))]
public Calendar FactoryCalendar { get; set; } // this is a DbSet
{

Entity Framework one-to-one relationships

I'm having some issues with understanding how to property set up one-to-one relationships for a Web API using Entity Framework Core.
Consider the following objects:
public class Car
{
[JsonIgnore]
public Int32 CarId { get; set; }
public virtual Horn Horn { get; set; }
public virtual ICollection<Wheel> Wheels { get; set; }
}
public class Horn
{
[JsonIgnore]
public Int32 HornId { get; set; }
public Sound Sound { get; set; }
}
public class Sound
{
// some other props
}
When I perform a query in my repository, one-to-many is by default excluded unless I use .Include(), however, for one-to-one properties, they are by default included when I serialize.
e.g. It gets really messy because I query for a car and it returns all it's sub-components in the JSON response.
I can set car.Horn = null, etc, but that seems difficult to maintain for complex objects. I would like it to function like .Include(), but by default be excluded (in case I do want to query the full object).
Edit: Note, this issue is recursive, the car pulls in horn which pulls in sound. On a real world example like a user table, the automatically pulled in data when serialized is huge unless specifically nullifying the child properties.
EDIT2:
Here is an example of a repository call that by default bring back all one-to-one properties:
var group = _context.Car.Include(c =>
c.Wheels).SingleOrDefault(u => u.CarId == id);
Note the Include works as expected for many-to-one, but this query, even when the Include is removed, will recursively return all child objects that are one-to-one.
It does appear some type of lazy loading is being introduced in EF CORE 2.1
This article should give you a hint.
https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
Mainly:
Turn lazy loading off for serialization
Lazy loading and serialization don’t mix well, and if you aren’t
careful you can end up querying for your entire database just because
lazy loading is enabled. Most serializers work by accessing each
property on an instance of a type. Property access triggers lazy
loading, so more entities get serialized. On those entities properties
are accessed, and even more entities are loaded. It’s a good practice
to turn lazy loading off before you serialize an entity. The following
sections show how to do this.
EDIT:
Here is the way to disable lazy loading for all entities. But note you have to achieve this several way, so check the other options in the article...
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Context mapping
modelBuilder.Entity<SessionFeedbackModel>(entity =>
{
entity.HasOne(s => s.Session).WithOne(p => p.Feedback)
.HasForeignKey<SessionFeedbackModel>(s => s.SessionId).OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<SessionQuestionModel>(entity =>
{
entity.HasOne(e => e.SessionResult).WithOne(e => e.SessionQuestion)
.HasForeignKey<SessionQuestionResultModel>(e => e.SessionQuestionId)
.OnDelete(DeleteBehavior.Restrict);
});
Models
public class SessionQuestionResultModel
{
public int Id { get; set; }
public int SessionQuestionId { get; set; }
public SessionQuestionModel SessionQuestion { get; set; }
}
public class SessionFeedbackModel
{
public int Id { get; set; }
public int SessionId { get; set; }
public SessionModel Session { get; set; }
}
EF Core 1.x or 2.x does not support 1 to 1 very well or at all, but it can be done this way, this would be radically different for EF 6.x.x

Serialization of Entity Framework objects with One to Many Relationship

I am attempting to use EF with Code First and the Web API. I don't have any problems until I get into serializing Many-to-Many relationships. When I attempt to execute the following web api method below I get the following error message:
public class TagsController : ApiController
{
private BlogDataContext db = new BlogDataContext();
// GET api/Tags
public IEnumerable<Tag> GetTags()
{
return db.Tags.AsEnumerable();
}
}
I get the following error:
'System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D'
with data contract name
'Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
I have read some SO articles (article 1, article 2) that the fix is to add the following attribute:
[DataContract (IsReference=true)]
but this has had no effect. Also using [IgnoreDataMember] does not have an effect. The only option that does seem to work is to set Configuration.ProxyCreationEnabled to false. Is this my only option? Am I missing something?
Sample POCO objects:
Tag
[DataContract(IsReference = true)]
public class Tag
{
public Tag()
{
this.Blogs = new HashSet<Blog>();
}
[Key]
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[IgnoreDataMember]
public virtual ICollection<Blog> Blogs { get; set; }
}
Blog
[DataContract(IsReference = true)]
public class Blog
{
public Blog()
{
this.Tags = new HashSet<Tag>();
}
[Key]
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[IgnoreDataMember]
public virtual ICollection<Tag> Tags { get; set; }
}
When you see an object like:
System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D
It is a runtime EF Generated version of a proxy to what would normally be considered a POCO object.
Entity Framework has created this object because it tracks when the objects has changed so when you call .SaveChanges() it can optimize what to do. The downfall of this is that you aren't actually using the specific object you defined, thus Data Contracts and Frameworks (Json.net) cannot use them as they would your original POCO object.
To Prevent EF from returning this object you have two choices (ATM):
First, Try turning off Proxy object creation on your DbContext.
DbContext.Configuration.ProxyCreationEnabled = false;
This will completely disable the create of Proxy objects for every query to the specific DbContext. (This does not affect the cached object in the ObjectContext).
Secondly, use EntityFramework 5.0+ with AsNoTracking()
(ProxyCreationEnabled is still available in EF 5.0 as well)
You should also be able to
DbContext.Persons.AsNoTracking().FirstOrDefault();
or
DbContext.Persons.
.Include(i => i.Parents)
.AsNoTracking()
.FirstOrDefault();
Instead of globally disabling proxy creation for the DbContext, this only turns it off per query. (This DOES affect the cached object in the ObjectContext, it is not cached)
I wanted to leave proxy creation inteact, and found that using the ProxyDataContractResolver seemed to resolve the issue for me. See msdn for a reference on how to use it in wcf, which isn't exactly WebAPI, but should get you going along the right path.

Why are my navigational properties null when retrieved from the database in EF 4.2 POCO?

I have a exceedingly simplistic data model (below). I am having trouble figuring out how I am to get my navigational properties to load from the database. I have no trouble getting them in, but the navigational property does not get set by EF it appears. I have seen several related questions, but they are slightly different or rather involved. I am looking for information on how navigational properties are treated by EF 4.2 (POCO). In the reading I've done, I got the impression that I would be able to access objects with foreign keys using navigational properties. Instead, my properties are coming back as either null or empty depending on if I instantiate my collection in the constructor.
public class AnimalDb : DbContext
{
public static AnimalDb Create(string fileName)
{
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
return new AnimalDb(fileName);
}
private AnimalDb(string fileName) : base(fileName) { }
public DbSet<Animal> Animals { get; set; }
}
public class Animal
{
public Animal()
{
Id = Guid.NewGuid();
Traits = new ObservableCollection<Trait>();
}
public Guid Id { get; set; }
public string Species { get; set; }
public string Name { get; set; }
public ObservableCollection<Trait> Traits { get; set; }
}
public class Trait
{
public Trait()
{
Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public string Name { get; set; }
}
And here is some (simple) code that uses it:
foreach (var animal in db.Animals)
{
foreach (var trait in animal.Traits)
{
//animal.Traits count is 0, so this does not run.
//However there are traits in the database, as my populate
//function is working fine.
Console.WriteLine("{0} is {1}", animal.Name, trait.Name);
}
}
----Edit Answer Summary----
Using the article and information provided in the answers below, I was able to discover I could either eagerly load using db.Animals.Include() or enable lazy loading. There is a trick to enabling lazy loading and being able to use it though. First to enable lazy loading I added:
db.Configuration.LazyLoadingEnabled = true;
Next I changed my Traits collection in the following manner:
public virtual ObservableCollection<Trait> Traits { get; set; }
Making it virtual allows the automatically generated proxy to lazily load Traits. That's it! IMHO I think the MSDN docs should shout this load and clear in the POCO EF 4.2 coding conventions. Again thanks for the help.
There are a few reasons that your wire-up methods may appear to have no data. To load related data you need to :
explicity load the data
meet the lazy loading requirements, or
use eager loading using Include()
My guess is that you turned off the virtual proxies. There is more on the requirements here:
http://msdn.microsoft.com/en-us/library/dd456855.aspx
If you don't use lazy loading you have to explicitly tell EF to load the relation with the Include method:
foreach (var animal in db.Animals.Include(a => a.Traits))
{
foreach (var trait in animal.Traits)
{
//...
}
}
You can read more about eager loading in this article.

Categories