序列化实体框架对象时出错

时间:2016-12-05 10:33:02

标签: c# entity-framework asp.net-web-api

背景

我有一个API,它使用两个表(标记和区域)从数据库中检索数据。每个令牌都有一个与Area对象相关的AreaID。我首先使用Entity Framework数据库。

问题:

当我尝试查询数据库以获取令牌(应包含对区域的引用)时。我收到一个错误:

Error getting value from 'Area' on 'System.Data.Entity.DynamicProxies.Token_51B85DEDCE118919D07024535C24194E890BF0A8499689750190733A8008D739'."

代码:

以下是由EF创建的模型:

public partial class Token
{
    public System.Guid ID { get; set; }
    public System.DateTime Expiry { get; set; }
    public System.Guid Reference { get; set; }
    public string IPAddress { get; set; }
    public string TokenValue { get; set; }
    public Nullable<System.Guid> Area_ID { get; set; }
    public virtual Area Area { get; set; }
}



public partial class Area
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]

    public Area()
    {
        this.Tokens = new HashSet<Token>();
    }
    public System.Guid ID { get; set; }
    public string Name { get; set; }
    public string UriPrefix { get; set; }
    public string Description { get; set; }
    public System.DateTime Created { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Token> Tokens { get; set; }
}

这是我查询数据库(修剪)的地方:

using (var dbContext = new APIEntities())
{
    try
    {
        tokenObject = dbContext.Tokens.Where(t => t.TokenValue == decryptedToken).FirstOrDefault(); 
        // the tokenObject is created but the Area property is null.
    } catch (Exception ex) {
        string message = ex.Message.ToString();
    }
}

以下是序列化JSON的方法:

 public HttpResponseMessage LogIntoAppWithToken()
    {
        string result = "";
        string jsonString = "failed to get JSON";
        string message = "";
        try
        {
            var re = Request;
            var headers = re.Headers;
            EncryptedToken encToken = new EncryptedToken();
            TokenChecker tokenChecker = new TokenChecker();
            encToken.tokenValue = "";

            if (headers.Contains("X-TOKEN"))
            {
                encToken.tokenValue = headers.GetValues("X-TOKEN").First();
            } 

            result = tokenChecker.IsTokenValid(encToken).ToString();
            Token retrievedToken = tokenChecker.GetTokenByTokenValue(encToken.tokenValue);
            jsonString = JsonConvert.SerializeObject(retrievedToken);
            // ERROR Here ^^

        } catch (Exception ex) {
            message = ex.Message.ToString();
        }
        return Request.CreateResponse(HttpStatusCode.OK, "Token is valid: " + result + ". " + " " + jsonString + " " + " Any error messages: " + message);
    }

我尝试了什么:

创建dbContext时,我使用了:

dbContext.Configuration.ProxyCreationEnabled = false;
dbContext.Configuration.LazyLoadingEnabled = false;

在WebApiConfig中:

        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
        config.Formatters.Remove(config.Formatters.XmlFormatter);

在Global.asax的Application_start中:

config.Formatters.JsonFormatter
    .SerializerSettings
    .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

我甚至尝试从tokenObject获取AreaID并查询数据库以手动获取区域&#39;然后将此检索到的Area对象添加到tokenObject(覆盖null版本):

Area areaObject = dbContext.Areas.Where(a => a.ID == tokenObject.Area_ID).FirstOrDefault();
tokenObject.Area = areaObject;

但这会导致自引用循环错误。 (这个想法更多的是用于测试而不是用作解决方案)。

我需要做些什么才能检索包含Area作为其属性之一的Token,然后序列化为JSON字符串?

修改1

有了@ ErikEJ的建议: enter image description here

我注意到Token对象现在包含Area对象,但Area对象包含一个包含Area的Token?这一直在继续...

根据我的设置,令牌肯定包含区域但区域不应包含令牌?

3 个答案:

答案 0 :(得分:1)

经过大量的搜索后,我找到了另一个适合我的answer

只需将[JsonIgnore]属性添加到受影响的属性即可。对于我的区域模型:

    [JsonIgnore]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Token> Tokens { get; set; }

答案 1 :(得分:0)

使用包含:

 tokenObject = dbContext.Tokens
      .Where(t => t.TokenValue == decryptedToken)
      .Include(t => t.Area) 
      .FirstOrDefault();

答案 2 :(得分:0)

如果您不想序列化引用的实体,请使用以下选项之一。

  1. 使用[JsonIgnore]注释特定属性。
  2. 要禁用特定属性的延迟加载,请不要将其设置为虚拟
  3. 为避免延迟加载所有实体,请将LazyLoadingEnabled设置为false 在您的bdContext类中。

    public SchoolDBEntities(): base("name=SchoolDBEntities")
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
    

    不用担心,您仍然可以通过调用Include方法来访问引用的实体。 示例:-

         var list = context.Customers
        .Include(c => c.Invoices)    
        .ToList();