0

Currently I am using [JsonIgnore] property to avoid circular references in the models. But I think, it will be applied in all implementations.

Right now I am using like this-

I have two Models:

public class Project
{
    public int ProjectID { get; set; }

    [Required]
    public string ProjectName { get; set; }

    public DateTime? EstimatedEndDate { get; set; }
    public DateTime? ProjectStartDate { get; set; }
    public string OrderNumber { get; set; }

    public string ProjectDescription { get; set; }
    public virtual List<ServiceObject> ServiceObjects { get; set; }

    [Required]
    public string RegardingUser { get; set; }

    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
    public bool IsProjectDeleted { get; set; }
    [NotMapped]
    public int? ServiceObjectTemplateID { get; set; }

}

One another Model is-

public class ServiceObject
{
    public int ServiceObjectID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string RegardingUser { get; set; }
    public int? ParentServiceObjectID { get; set; }
    [ForeignKey("ParentServiceObjectID")]
    public virtual List<ServiceObject> ServiceObjects { get; set; }
    public int ProjectID { get; set; }
    [JsonIgnore]
    public virtual Project Project { get; set; }<--------------------------- 
    public virtual List<ServicePicture> ServicePictures { get; set; }
    public bool IsServiceObjectDeleted { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
}

But I want to apply JsonIgnore property or any other property In Linq to Sql query itself. I mean I want to apply it dynamically.

The Query is like-

var projectList = (from pro in context.Projects.All
                   select pro).ToList();

I want to apply conditionally.

Thanks in advance.

Aron
  • 15,464
  • 3
  • 31
  • 64
Abhinav
  • 353
  • 1
  • 7
  • 19

2 Answers2

2

XY problem. Your actual problem is "I want to control at runtime what properties get serialized".

The most common method of controlling what properties are serialized is using the JsonIgnoreAttribute. However as others have commented, attributes are applied at compile time, and cannot normally be modified at runtime (nor would you want to, since it would in effect be a global setting being changed at runtime, you would run into a ton of Threading issues).

Instead, the answer is to use the IContractResolver interface to change the serialization behavior.

public class OmitPropertyContractResolver
       : IContractResolver
{
    private readonly string[] _ignoredProperties;
    private readonly IContractResolver _resolver;

    public OmitPropertyContractResolver(IContractResolver resolver, params string[] ignoredProperties)
    {
        _ignoredProperties = ignoredProperties;
        _resolver = resolver;        
    }
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = _resolver.CreateProperties(type, memberSerialization);
        return properties
               .Where(p => _ignoredProperties.Contains(p.Name) == false)
               .ToList();
    }
}

The other possibility is to use ReferenceLoopHandling setting in your Json.net settings.

var serializer = new JsonSerializer(
                      new JsonSerializerSetting()
                               { 
                                   ReferenceLoopHandling = ReferenceLoopHandling.Ignore 
                               } );

This will serialize each object only once.

The last option is to use ItemReferenceLoopHandling. This is a non-standard way of serializing (and deserializing) Object-Graphs (note, graph and not tree) which have circular references.

The disadvantage of this method is that it is non-standard, and might not work on the client end.

Aron
  • 15,464
  • 3
  • 31
  • 64
1

Ok, so based on your inputs and as I suspected, what you're seeing is a typical EF Code First issue. EF generates dynamic proxies for each of your entity so that it can track changes to it. However, this makes serialization difficult since they are created as dynamic objects. There are few ways to avoid this:

Option A: Disable proxy creation just for the particular query:

// disable proxy creation just for the duration of query
context.Configuration.ProxyCreationEnabled = false;

// do the query, serialize, go crazy!

// enable proxy again. or if your context goes out of scope after this call, then you can ignore re-enabling.
context.Configuration.ProxyCreationEnabled = true;

Option B: If you are not going to modify and update the entity at some later point

var entity = context.Where(...);  // or whatever query

// detach entity
context.Entry(entity).State = EntityState.Detached; 

Option C: Avoid proxies by modifying type definition:

If you create sealed type or type without virtual properties, EF will not create a proxy since there is nothing for the proxy to do.

public sealed class Project {...}

// or make it non virtual
...
public List<ServiceObject> ServiceObjects { get; set; }
...

Note that in this case, you have to load related objects manually:

context.Projects.Include(p => p.ServiceObjects).ToList();

Option D: Disable proxy creation permanently:

// in your initializer or DbContext class' constructor
DbContext.Configuration.ProxyCreationEnabled = false;

Note: If proxy creation is disabled, EF will not tracked changes automatically. You can manually track them using the Snapshot method.

You can follow one or more of these options depending on your design. I usually stick with option A or B.

Mrchief
  • 75,126
  • 20
  • 142
  • 189