使用EntityObjects进行Ajax绑定的Telerik MVC Grid获取循环引用异常

时间:2011-10-25 11:45:30

标签: c# telerik telerik-mvc circular-reference

4 个答案:

答案 0 :(得分:1)

第一个解决方案适用于网格编辑模式,但是我们对已经包含循环引用的对象行的网格加载存在同样的问题,为了解决这个问题,我们需要创建一个新的IClientSideObjectWriterFactory和一个新的IClientSideObjectWriter。 。 这就是我的工作:

1-创建一个新的IClientSideObjectWriterFactory:

public class JsonClientSideObjectWriterFactory : IClientSideObjectWriterFactory
{
    public IClientSideObjectWriter Create(string id, string type, TextWriter textWriter)
    {
        return new JsonClientSideObjectWriter(id, type, textWriter);
    }
}

2-创建一个新的IClientSideObjectWriter,这次我没有实现接口,我继承了ClientSideObjectWriter并重写了AppendObject和AppendCollection方法:

public class JsonClientSideObjectWriter : ClientSideObjectWriter
{
    public JsonClientSideObjectWriter(string id, string type, TextWriter textWriter)
        : base(id, type, textWriter)
    {
    }

    public override IClientSideObjectWriter AppendObject(string name, object value)
    {
        Guard.IsNotNullOrEmpty(name, "name");

        var data = JsonConvert.SerializeObject(value,
            Formatting.None,
            new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore,
                    ContractResolver = new PropertyNameIgnoreContractResolver()
                });

        return Append("{0}:{1}".FormatWith(name, data));
    }

    public override IClientSideObjectWriter AppendCollection(string name, System.Collections.IEnumerable value)
    {
    public override IClientSideObjectWriter AppendCollection(string name, System.Collections.IEnumerable value)
    {
        Guard.IsNotNullOrEmpty(name, "name");

        var data = JsonConvert.SerializeObject(value,
            Formatting.Indented,
            new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore,
                    ContractResolver = new PropertyNameIgnoreContractResolver()
                });

        data = data.Replace("<", @"\u003c").Replace(">", @"\u003e");

        return Append("{0}:{1}".FormatWith((object)name, (object)data));
    }
}

注意:替换它,因为网格在编辑模式下为客户端模板呈现html标记,如果我们不编码,则浏览器将呈现标记。如果没有使用替换字符串对象,我还没有找到workarround。

3-在Global.asax.cs上的Application_Start上我注册了我的新工厂:

DI.Current.Register<IClientSideObjectWriterFactory>(() => new JsonClientSideObjectWriterFactory());

它适用于Telerik拥有的所有组件。我唯一没有改变的是PropertyNameIgnoreContractResolver,它与EntityFramework类相同。

答案 1 :(得分:0)

我将新调用放入Application_Start中以实现CustomGridActionResultFactory但是create方法从未调用过...

答案 2 :(得分:0)

我采取了一种稍微不同的方法,我认为这可能更容易实施。

我所做的就是将一个扩展的[Grid]属性应用于网格json返回方法,而不是普通的[GridAction]属性

public class GridAttribute : GridActionAttribute, IActionFilter
  {    
    /// <summary>
    /// Determines the depth that the serializer will traverse
    /// </summary>
    public int SerializationDepth { get; set; } 

    /// <summary>
    /// Initializes a new instance of the <see cref="GridActionAttribute"/> class.
    /// </summary>
    public GridAttribute()
      : base()
    {
      ActionParameterName = "command";
      SerializationDepth = 1;
    }

    protected override ActionResult CreateActionResult(object model)
    {    
      return new EFJsonResult
      {
       Data = model,
       JsonRequestBehavior = JsonRequestBehavior.AllowGet,
       MaxSerializationDepth = SerializationDepth
      };
    }
}

public class EFJsonResult : JsonResult
  {
    const string JsonRequest_GetNotAllowed = "This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.";

    public EFJsonResult()
    {
      MaxJsonLength = 1024000000;
      RecursionLimit = 10;
      MaxSerializationDepth = 1;
    }

    public int MaxJsonLength { get; set; }
    public int RecursionLimit { get; set; }
    public int MaxSerializationDepth { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
      if (context == null)
      {
        throw new ArgumentNullException("context");
      }

      if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
          String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
      {
        throw new InvalidOperationException(JsonRequest_GetNotAllowed);
      }

      var response = context.HttpContext.Response;

      if (!String.IsNullOrEmpty(ContentType))
      {
        response.ContentType = ContentType;
      }
      else
      {
        response.ContentType = "application/json";
      }

      if (ContentEncoding != null)
      {
        response.ContentEncoding = ContentEncoding;
      }

      if (Data != null)
      {
        var serializer = new JavaScriptSerializer
        {
          MaxJsonLength = MaxJsonLength,
          RecursionLimit = RecursionLimit
        };

        serializer.RegisterConverters(new List<JavaScriptConverter> { new EFJsonConverter(MaxSerializationDepth) });

        response.Write(serializer.Serialize(Data));
      }
    }

将此与我的序列化程序Serializing Entity Framework problems结合使用,您可以采用一种简单的方法来避免循环引用,还可以选择序列化多个级别(我需要)

注意:Telerik最近为我添加了这个虚拟的CreateActionResult,因此您可能需要下载最新版本(不确定,但我认为可能是1.3 +)

答案 3 :(得分:0)

另一个好的模式是不要避免从模型中创建ViewModel。 包含ViewModel是一个很好的模式。它使您有机会对模型进行最后一刻的UI相关调整。例如,您可以调整bool以获得关联的字符串YN,以帮助使UI看起来更好,反之亦然。 有时ViewModel与模型完全相同,复制属性的代码似乎没有必要,但模式是好的,坚持使用是最好的做法。