JSON Serializer尝试访问一个早已消失的EntityFramework上下文,对于我没有要求的数据,或者关心

beg*_*boy 1 c# json entity-framework json.net

我想做什么?

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

http://localhost:56680/api/Booking/Countries
Run Code Online (Sandbox Code Playgroud)

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

SQL数据库表结构

我从我的数据库中包含屏幕截图的原因是因为汽车是这里的麻烦制造者.

国家和汽车彼此之间有许多关系,由CountryCars(基本上是Vignettes)代表.


这些是相应的域模型:

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

代码和结构

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

DAO代码:

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

        catch (Exception ex)
        {

            return null;
        }
    }
Run Code Online (Sandbox Code Playgroud)

因此,该请求的处理方式如下:

Web -> API Layer -> Business Layer -> Data Access Layer -> EF
Run Code Online (Sandbox Code Playgroud)

并回来像这样:

Web <- API Layer <- Business Layer <- Data Access Layer <- EF
Run Code Online (Sandbox Code Playgroud)

我花了很长时间才明白,在最后一次返回时,当JSON.net将我的Object序列化为.json时,它突然试图拉动这一步:

       API Layer ----------------------------------------> EF
Run Code Online (Sandbox Code Playgroud)

哪个让我感到困惑.对不起,但序列化程序不能只是滚动并在该字段中序列化一个空数组/ 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)"
    }
}
}
Run Code Online (Sandbox Code Playgroud)

}


我试过的东西

我已经尝试过.Include(c => c.Cars),但是这会立即给我CS0311错误,但这仍然不是理想的代码.

我也想过用DTO做这件事,但是我真的不喜欢用更多的课程来混淆我的后端的想法.

我还有另外两个看着这个问题的开发人员也感到困惑.

AAA*_*ddd 5

将此形式化为答案:似乎序列化程序试图通过virtual属性延迟加载有问题的集合.

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

db.Configuration.LazyLoadingEnabled = false
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是忽略序列化通过[JsonIgnore]适当的属性.但是,将其推向您的域层会闻到.

或者您可以投影到DTO以更好地封装您想要和不想要的数据.