3

I have a database with Car Histories. Each car history has multiple images associated with it. I am using NHibernate 2.2 to set up the relationships and the CarHistory mapping contains a bag for the images:

<bag name="Photos" table="DetailPhoto" cascade="all" lazy="true">
  <key column="CAR_DETAIL_ID"/>
  <one-to-many class="DetailPhoto"/>
</bag>

I have an iPad app which communicates with the server using JSON. I want to load all of the car history items into a list on the iPad and I don't want to include the photos when loading the list because it slows down the data retrieval which is why I have made the Photos lazy.

When I try and use JsonConvert.SerializeObject to serialize my list of Car Histories I get a lazy initialization exception which is because I have loaded my objects and closed the session already because I don't need the photos and the JsonSerializer is touching all of the properties on the object.

I would like to return the Json data to the client without the photos but I cannot use the ignore JsonProperty on my object because I want to load this collection in other situations.

I have tried this but it just does the same thing giving me the lazy initialization exception: http://www.royjacobs.org/2011/07/27/using-json-net-to-serialize-proxied-nhibernate-objects/

This is my CarHistory (CarDetail) class

 public class CarDetail
{
    [JsonProperty("id")]
    public virtual int Id { get; set; }

    [JsonProperty("carId")]
    public virtual int CarId { get; set; }

    [JsonProperty("date")]
    public virtual DateTime ? Date { get; set; }

    [JsonProperty("details")]
    public virtual string Details { get; set; }

    [JsonProperty("photos")]
    public virtual IList<DetailPhoto> Photos { get; set; }
}

So my question is how can I retrieve a list of CarHistories without the associated Photos in some circumstances and not others?

Asagohan
  • 583
  • 5
  • 19

2 Answers2

2

For cases like this you often want to come up with a layer in-between your "connected" domain objects and the serialization format. I usually create a data transfer object, which is similar to the domain model object, but usually doesn't have direct associations and instead just has lists of IDs. I then use Automapper to map between the data transfer object and the domain object.

codekaizen
  • 26,990
  • 7
  • 84
  • 140
  • So does this mean that I would need one DTO for the listing of a CarHistory, and a separate DTO for the display of a CarHistory? And in the listing DTO I would ignore the JSON property and in the display DTO I include it? I was trying to avoid this because I would end up with an explosion of DTO and it would make everything more complicated. – Asagohan Sep 25 '12 at 05:27
  • The way that I had it was good because on the client side I am using RESTKit which maps the JSON data back into Objective C objects with all of the associations. If I only have IDs I don't think I can do that anymore can I? – Asagohan Sep 25 '12 at 05:34
  • You can model the list as a list of CarHistory DTOs in this case, of course, since the item DTO is known and serializable. It's when you get into cases like, for example, the `CarId` which really should be `Car` in the domain model. By knowing the Id of the car and having the Car entity known on both sides, you don't then need to serialize it, and therefore you don't need to load it, lazily or otherwise. Once you serialize the DTO and send it, you can deserialize it (using whatever) and reverse map it back to a domain object. – codekaizen Sep 25 '12 at 06:27
  • Not true, you do not need to introduce DTOs, you just have to configure the JSON-Serializer properly, see i.e. https://stackoverflow.com/a/35088817/717732 – quetzalcoatl Nov 02 '17 at 09:54
  • You do not need to, but if your domain objects are more than just property holders (aka anemic domain model), keeping the state transitions due to deserialization separate from those based on domain logic is more complex than just building DTOs and mapping. – codekaizen Nov 02 '17 at 09:58
  • What you say is very true, but the question was very specific. It was about how to, in NHibernate, fetch/and/or/serialize without triggering the lazy-loading. Let me quote the last line of the question, `So my question is how can I retrieve a list of CarHistories without the associated Photos in some circumstances and not others?` and even better in the title. Your answer strongly suggests that this is not possible, and even if OP is all good with your suggestion, that part of your answer is _**just plain wrong**_ and may harm other people that need exactly what was asked. – quetzalcoatl Nov 05 '17 at 17:16
0

The default contract-serializer tries serializing everything, and when it hits a thing-not-yet-loaded, it does not recognize it and attempts to serialize it. That in turn forces the proxy to attempt loading the data, which causes performance problems (1 + n + n^2 + .. queries) or simply straight away throws an exception (when session's already closed) - and that's what you have observed in the first place.

Using a custom the contract-resolver, you will be able to instruct the serializer to not serialize collections and properties that were not loaded. Or throw your better an exception, or log it, whatever you want in your case.

See similar problem: JSON.Net Serialization of NHibernate Proxies (NH 3.3.2.4000) - you can find there an example of a custom ContractResolver.

It's not complete for your needs though, since your problem is different, so when writing your own ContractResolver, inside it you should filter properties-to-be-serialized by, for example:

var propertyInfo = (PropertyInfo)member;
var value = propertyInfo.GetValue(target);
var shouldSerialize = NH.NHibernateUtil.IsInitialized(value);

which produces false when the property contains a collection or object-reference that was proxied and was not loaded yet.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107