JSON序列化程序会尝试访问一个早已消失的EntityFramework上下文,以获取我不需要或关心的数据

时间:2019-02-27 10:29:06

标签: c# json entity-framework json.net

我要做什么?

我正在向我的C#后端(MSSQL + EF6)发送API调用,

http://localhost:56680/api/Booking/Countries

此API调用的关注点只是从我的数据库中获取国家/地区。

SQL Database Table Structure

之所以要包含数据库中的屏幕截图,是因为Cars是这里的麻烦制造者。

国家和汽车之间相互联系,以CountryCars(基本上就是小插图)为代表。


这些是相应的域模型:

Car.cs

public class Car
{
    public int Id { get; set; }
    public Guid GUID { get; set; }
    public string Model { get; set; }
    public virtual VehicleType VehicleType { get; set; } 
    public virtual Location Location { get; set; }
    [StringLength(255)]
    [Index(IsUnique=true)]
    public string LicensePlate { get; set; }
    public int NrOfSeats { get; set; }
    public Equipment Equipment { get; set; }
    public virtual ICollection<Country> Vignette { get; set; }
    public bool Available { get; set; }
    public virtual FuelType FuelType { get; set; }
    public string ReasonOfAbsence { get; set; }
    public bool isForCarSharing { get; set; }
    public int? SharepointId { get; set; }

    public Car()
    {
        Location = new Location();
        Equipment = new Equipment();
        FuelType = new FuelType();
        VehicleType = new VehicleType();
        Vignette = new List<Country>();
        Available = true;
    }

    public Car(Car car)
    {
        Id = car.Id;
        GUID = car.GUID;
        Model = car.Model;
        VehicleType = car.VehicleType;
        Location.Name = car.Location.Name;
        LicensePlate = car.LicensePlate;
        NrOfSeats = NrOfSeats;
        Equipment = car.Equipment;
        Vignette = car.Vignette;
        Available = car.Available;
        FuelType.FuelName = car.FuelType.FuelName;
        ReasonOfAbsence = car.ReasonOfAbsence;
    }

}

Country.cs

public class Country
{
    public int Id { get; set; }

    public Guid GUID { get; set; }

    public string CountryName { get; set; }

    public string CountryCode { get; set; }

    public virtual ICollection<Car> Cars { get; set; }

    public Country()
    {

    }

    public Country(int id, string cc)
    {
        Id = id;
        CountryCode = cc;
    }

    public Country(int id, string cc, string cn)
    {
        Id = id;
        CountryCode = cc;
        CountryName = cn;
    }

    public Country(int id, Guid guid, string countryCode, string countryName)
    {
        Id = id;
        GUID = guid;
        CountryCode = countryCode;
        CountryName = countryName;
    }
}

代码与结构

API控制器代码:

[HttpGet]
    [Route("Countries")]
    public ICollection<Country> GetCountries()
    {
        try
        {
            return BL.Instance.GetAllCountries();

        }
        catch(Exception ex)
        {
            Log.Error("Getting Countries failed: (" + User.Identity.Name + ") " + ex.Message + ex.StackTrace);
            return null;
        }
    }

DAO代码:

public ICollection<Country> GetAllCountries()
    {
        try
        {
            using (var _dbContext = new CarSharingContext())
            {
                return _dbContext.Countries.ToList();
            }
        }

        catch (Exception ex)
        {

            return null;
        }
    }

因此,该请求的内容如下:

Web -> API Layer -> Business Layer -> Data Access Layer -> EF

然后像这样回来:

Web <- API Layer <- Business Layer <- Data Access Layer <- EF

我花了很长时间才了解到,在最后一次返回时,当JSON.net将我的Object序列化为.json时,它突然尝试拉动此举:

       API Layer ----------------------------------------> EF

哪个让我感到困惑。我想知道为什么序列化器不能只序列化该字段中的空数组/ null,而不能尝试一直返回到早已消失的上下文?我意识到它正在尝试做的事情,而且我看到这对于具有填充良好的对象非常有用。

但是,在我看来,串行器不应该开始回馈更多数据。这是我100%在任何时候都没有要求的电话。

这是我从API返回的JSON:

{
"Message": "An error has occurred.",
"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": null,
"InnerException": {
    "Message": "An error has occurred.",
    "ExceptionMessage": "Error getting value from 'Cars' on 'System.Data.Entity.DynamicProxies.Country_DAAADB66BC9D631592190A398D1C864E889F15DB34CB65EDB14A5FEB021BE73A'.",
    "ExceptionType": "Newtonsoft.Json.JsonSerializationException",
    "StackTrace": "   bei Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)\r\n   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)\r\n   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n   bei Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n   bei System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n   bei System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n   bei System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)\r\n   bei System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---\r\n   bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   bei System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()",
    "InnerException": {
        "Message": "An error has occurred.",
        "ExceptionMessage": "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.",
        "ExceptionType": "System.ObjectDisposedException",
        "StackTrace": "   bei System.Data.Entity.Core.Objects.ObjectContext.get_Connection()\r\n   bei System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)\r\n   bei System.Data.Entity.Core.Objects.ObjectQuery`1.Execute(MergeOption mergeOption)\r\n   bei System.Data.Entity.Core.Objects.DataClasses.EntityCollection`1.Load(List`1 collection, MergeOption mergeOption)\r\n   bei System.Data.Entity.Core.Objects.DataClasses.EntityCollection`1.Load(MergeOption mergeOption)\r\n   bei System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.DeferredLoad()\r\n   bei System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.LoadProperty[TItem](TItem propertyValue, String relationshipName, String targetRoleName, Boolean mustBeNull, Object wrapperObject)\r\n   bei System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.<>c__DisplayClass7`2.<GetInterceptorDelegate>b__1(TProxy proxy, TItem item)\r\n   bei System.Data.Entity.DynamicProxies.Country_DAAADB66BC9D631592190A398D1C864E889F15DB34CB65EDB14A5FEB021BE73A.get_Cars()\r\n   bei GetCars(Object )\r\n   bei Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)"
    }
}
}

我尝试过的东西

我尝试做.include(c => c.Cars),但这立即给我CS0311错误,而且无论如何这不是可取的代码。

我还考虑过使用DTO进行此操作,但是我真的不喜欢用更多的类使后端更加混乱的想法。

我还有另外两个开发人员也在研究这个问题。

1 个答案:

答案 0 :(得分:5)

将其形式化为一个答案:似乎序列化程序正在尝试通过virtual属性来延迟加载有问题的集合。

您可以尝试对该查询禁用EF上的延迟加载。我个人在全局禁用它,因为它很邪恶:

db.Configuration.LazyLoadingEnabled = false

另一种解决方案是忽略通过[JsonIgnore]在适当属性上进行的序列化。但是,将其向下推送到您的 Domain Layer 会闻起来。

或者您可以投影到DTO,以更好地封装所需和不需要的数据。