我要做什么?
我正在向我的C#后端(MSSQL + EF6)发送API调用,
http://localhost:56680/api/Booking/Countries
此API调用的关注点只是从我的数据库中获取国家/地区。
之所以要包含数据库中的屏幕截图,是因为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进行此操作,但是我真的不喜欢用更多的类使后端更加混乱的想法。
我还有另外两个开发人员也在研究这个问题。
答案 0 :(得分:5)
将其形式化为一个答案:似乎序列化程序正在尝试通过virtual
属性来延迟加载有问题的集合。
您可以尝试对该查询禁用EF上的延迟加载。我个人在全局禁用它,因为它很邪恶:
db.Configuration.LazyLoadingEnabled = false
另一种解决方案是忽略通过[JsonIgnore]
在适当属性上进行的序列化。但是,将其向下推送到您的 Domain Layer 会闻起来。
或者您可以投影到DTO,以更好地封装所需和不需要的数据。