Unexpected SerializationException when using IRequiresRequestStream - deserialization

We have a request Dto which is defined as this:
public class AddDocumentByUploadBinaryRequest: AddDocumentRequest, IRequiresRequestStream
{
public string FileName { get; set; }
public Stream RequestStream { get; set; }
}
And AddDocumentRequest class definition is:
public class AddDocumentRequest: IReturn<ResponseBase>
{
public Guid Id { get; set; }
public string[] Tags { get; set; }
public Guid? LotId { get; set; }
public Guid? ContactId { get; set; }
// never need it to attach document to existing bill
//public Guid? BillId { get; set; }
public Guid? FolioId { get; set; }
public Guid? JobTaskId { get; set; }
public Guid? TaskId { get; set; }
public Guid? TenantInvoiceId { get; set; }
public Guid? InspectionTaskId { get; set; }
public Guid? InspectionReportAreaId { get; set; }
public Guid? InMessageId { get; set; }
public DocumentTypes? DocumentType { get; set; }
public EventLinkTypes? DocumentArea { get; set; }
public Guid? MessageId { get; set; }
public Guid? LinkId {
get {
switch (DocumentArea) {
//case EventLinkTypes.Bill:
// return BillId;
case EventLinkTypes.Contact:
return ContactId;
case EventLinkTypes.Inspection:
return InspectionTaskId;
case EventLinkTypes.InspectionReport:
return InspectionTaskId;
case EventLinkTypes.InspectionReportArea:
return InspectionReportAreaId;
case EventLinkTypes.Job:
return JobTaskId;
case EventLinkTypes.Lot:
return LotId;
case EventLinkTypes.Task:
return TaskId;
case EventLinkTypes.Message:
return MessageId;
default:
return FolioId;
}
}
}
}
The API works most of the time but occassionally, we get "Unable to bind to request" error with the details like this:
Unable to bind to request 'AddDocumentByUploadBinaryRequest'; Error code: SerializationException; Field name: ���iD�����; Message: '�Z~�l�_$�6j�Ǒ�Xƾ��ԯ㵂���;W;/�����k� 6��;ׯG$�O�`�V�j��a�Z�[S幉>�>����[An��O
X�n$�<�p�H��GN��[���c�|5�#��
?�' is an Invalid value for '���iD�����'
The captured error message suggests that the binary body was unexpectedly deserialized and failed.
The client device always uses Content-Type: application/octet-stream header. I have read relevant source code of ServiceStack but I couldn't think of why the binary body would still go into deserialization.
Any ideas would be appreaciated.

Related

How to fix "Exception: Invalid channel" error in my API

I'm trying to save a object Using HTTP Post Method in my API Controller, but it's returning a error, i've tried to do the same in another controler and it works. I hope you may help!
The Request that I tried:
{
"integrationServiceHost": "10.80.80.10",
"RouterHost": "10.80.80.10",
"Enabled": "false",
"IntegrationServiceRemotePort": "1234",
"RouterSocketPort":"1234",
"RouterRemotePort":"1234",
"IDChannelBehavior":"10",
"IDEPS":"1",
"Description":"Teste Whats",
"IDChannel":"0"
}
ChannelWhatsapp Class:
public class ChannelWhatsApp : Channel
{
public ChannelWhatsApp();
[Column("WHATSAPP_UserName")]
public string UserName { get; set; }
[Column("WHATSAPP_Password")]
public string Password { get; set; }
[Column("WHATSAPP_DisplayName")]
public string DisplayName { get; set; }
}
url : http://172.19.22.81:5000/api/channelWhatsapp/saveDto
Channel Class :
public abstract class Channel : NotifyPropertyChanged
{
public Channel();
public virtual ICollection<Interaction> Interaction { get; set; }
public virtual ICollection<Schedule> Schedule { get; set; }
public virtual ICollection<Queue> Queue { get; set; }
public virtual ICollection<Session> Session { get; set; }
public virtual ChannelBehavior ChannelBehavior { get; set; }
public virtual EPS EPS { get; set; }
public DateTime? DtLastChange { get; set; }
[MaxLength(100)]
public string IntegrationServiceHost { get; set; }
[MaxLength(100)]
public string RouterHost { get; set; }
public bool Enabled { get; set; }
public int? IntegrationServiceRemotePort { get; set; }
public int? RouterSocketPort { get; set; }
public int? RouterRemotePort { get; set; }
[ForeignKey("ChannelBehavior")]
public int IDChannelBehavior { get; set; }
[ForeignKey("EPS")]
public int IDEPS { get; set; }
[MaxLength(200)]
public string Description { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int IDChannel { get; set; }
[NotMapped]
public string IconName { get; set; }
[NotMapped]
public byte? PendingPauseCode { get; set; }
[NotMapped]
public bool IsPausePending { get; set; }
[NotMapped]
public SystemUserOperator Operator { get; set; }
[NotMapped]
public DateTime StateMomment { get; set; }
[NotMapped]
public UserStateType CurrentState { get; set; }
public virtual ICollection<ChannelSkill> ChannelSkills { get; set; }
[NotMapped]
public ChannelTypeType Type { get; set; }
}
My Controller:
[Route("api/ChannelWhatsapp/{Action}")]
[ApiController]
public class ChannelWhatsappController : IrideController<ChannelWhatsApp>
{
public ChannelWhatsappController(IrideContext context) : base(context) { }
[HttpPost("")]
[ActionName("saveDto")]
public IActionResult saveDto([FromBody] ChannelWhatsApp entity)
{
try
{
////Obtem o data
//entity.DtLastChange = IrideUtil.getDate() ;
//_context.Set<ChannelWhatsApp>().Update(entity);
//_context.SaveChanges();
return Ok(entity);
}
catch (Exception ex)
{
return Ok(ex.ToString());
}
}
}
returned error:
Exception: Invalid channel IrideCM.Model.Channel..ctor()
IrideCM.Model.ChannelWhatsApp..ctor() lambda_method(Closure )
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader
reader, JsonObjectContract objectContract, JsonProperty
containerMember, JsonProperty containerProperty, string id, out bool
createdFromNonDefaultCreator) in JsonSerializerInternalReader.cs
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader
reader, Type objectType, JsonContract contract, JsonProperty member,
JsonContainerContract containerContract, JsonProperty containerMember,
object existingValue) in JsonSerializerInternalReader.cs
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader
reader, Type objectType, bool checkAdditionalContent) in
JsonSerializerInternalReader.cs
When posting a payload to a controller in the request's body, you need to make sure the payload's structure matchs the structure of the expected class.
In your case, it does not match at all. You just need to post a payload with the same type as the type expected in the FromBody param of your endpoint.

Returning Entity with its children

Hi I am trying to return all vehicles with their recorded mileage through an api using ASP.Net Core with the following code:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.Include(m=>m.Mileages).ToList();
}
However this only returns the first vehicle with its mileages and not the others (there are five dummy vehicles in the db all with an initial mileage).
If I change the code to:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.ToList();
}
it returns the full list of vehicles but no mileage.
My class files are:
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<Mileage> Mileages { get; set; }
}
and
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
}
thanks for looking!
Tuppers
you can have them auto-load (lazy loading) using proxies... but for that, your foreign entities and collections must be marked virtual in your POCOs:
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public virtual ICollection<Mileage> Mileages { get; set; }
}
The proxy creation and lazy loading turned on, but that's the default in EF6.
https://msdn.microsoft.com/en-us/data/jj574232.aspx
Let me know if this works.
Well after a lot of searching I managed to find a solution. I used the following:
[HttpGet]
public IEnumerable<VehicleDto> Get()
{
var query = _context.Vehicles.Select(v => new VehicleDto
{
Registration = v.Registration,
Make = v.Make,
Model = v.Model,
Marked = v.Marked,
Mileages = v.Mileages.Select(m => new MileageDto
{
MileageDate = m.MileageDate,
RecordedMileage = m.RecordedMileage
})
.ToList(),
})
.ToList();
return (IEnumerable<VehicleDto>) query.AsEnumerable();
this doesn't seem to be the most elegant way of doing this, if anyone could offer any advice but it does return what is required.
The DTO's look like:
public class VehicleDto
{
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<MileageDto> Mileages { get; set; }
}
and
public class MileageDto
{
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
}
Thanks for taking the time to look at this
Tuppers

web api entity framework: return all the data whatever the query is

I'm using Entity Framework in asp.net web api and i have a problem with IHttpActionResult, it is returning all the the database data even when i just want to get data by id.
I have a Player class, and a PlayerStat classe, each player many rows of stats according to the number of season he played.
If i try to get a Player Name, it is fine, but when i try to access player Stats, evry thing goes mad !
instead of having the player stat for each season, i get all the data in my database
public partial class Player
{
public Player()
{
this.PlayAt = new HashSet<PlayAt>();
this.PlayerStat = new HashSet<PlayerStat>();
}
public int IDPlayer { get; set; }
public string NamePlayer { get; set; }
public Nullable<int> Height { get; set; }
public Nullable<int> Weight { get; set; }
public string Country { get; set; }
public Nullable<System.DateTime> DateBirth { get; set; }
public string StartPlay { get; set; }
public virtual ICollection<PlayAt> PlayAt { get; set; }
public virtual ICollection<PlayerStat> PlayerStat { get; set; }
}
public partial class PlayerStat
{
public int IDPlayer { get; set; }
public string IDSeason { get; set; }
public int Age { get; set; }
public string IDTeam { get; set; }
public string Pos { get; set; }
public Nullable<int> G { get; set; }
public Nullable<int> GS { get; set; }
/*Other Stat like GS*/
public virtual Player Player { get; set; }
public virtual Season Season { get; set; }
public virtual Team Team { get; set; }
}
}
The request:
public IHttpActionResult GetPlayer(int id)
{
var statsQuery = from pl in db.Player where pl.IDPlayer == id select pl.PlayerStat;
if (statsQuery == null)
{
return NotFound();
}
return Ok(statsQuery);
}

Getting ObjectContext error even after calling ToList

When calling the method directly below I get a ObjectDisposedException when calling Mapper.Map with the retrieved list.
System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
public IEnumerable<Models.Provider> Get(string owner)
{
List<Data.Models.Provider> providers;
using (var db = new Data.ProviderDirectoryContext())
{
providers = db.Providers.Where(p => p.Owner.Name == owner).ToList();
}
var dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
return dtoProviders;
}
I previously had the code like this (below), I wasn't getting an error, but the database was getting pounded when doing the mapping, and it was taking too long. I don't want to hit the database, when doing the mapping.
public IEnumerable<Models.Provider> Get(string owner)
{
using (var db = new Data.ProviderDirectoryContext())
{
var providers = db.Providers.Where(p => p.Owner.Name == owner).ToList();
var dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
return dtoProviders;
}
}
How can I retrieve all the data before doing the mapping?
Here is the DbContext and the Data.Models.Provider for your reference.
public class ProviderDirectoryContext : DbContext
{
public DbSet<Owner> Owners { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<LocationAuditLog> LocationAuditLog { get; set; }
public DbSet<Office> Offices { get; set; }
public DbSet<OfficePhoneNumber> OfficePhoneNumbers { get; set; }
public DbSet<OfficeAuditLog> OfficeAuditLog { get; set; }
public DbSet<OfficeDay> OfficeDays { get; set; }
public DbSet<Provider> Providers { get; set; }
public DbSet<ProviderPhoneNumber> ProviderPhoneNumbers { get; set; }
public DbSet<ProviderAuditLog> ProviderAuditLog { get; set; }
public DbSet<ProviderType> ProviderTypes { get; set; }
public DbSet<ProviderSpecialty> ProviderSpecialties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Provider>().HasRequired(cn => cn.Owner).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Office>().HasRequired(cn => cn.Owner).WithMany().WillCascadeOnDelete(false);
}
}
public class Provider
{
public int Id { get; set; }
public int OwnerId { get; set; }
public virtual Owner Owner { get; set; }
public int? ProviderTypeId { get; set; }
public virtual ProviderType ProviderType { get; set; }
public int? ProviderSpecialtyId { get; set; }
public virtual ProviderSpecialty ProviderSpecialty { get; set; }
[Required]
[StringLength(75)]
public string FirstName { get; set; }
[StringLength(75)]
public string MiddleName { get; set; }
[Required]
[StringLength(75)]
public string LastName { get; set; }
[StringLength(100)]
public string EmailAddress { get; set; }
public virtual ICollection<ProviderPhoneNumber> PhoneNumbers { get; set; }
public string Note { get; set; }
public DateTime? InactiveOn { get; set; }
public int OfficeId { get; set; }
public virtual Office Office { get; set; }
public virtual ICollection<ProviderAuditLog> AuditLog { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[Required]
[StringLength(75)]
public string CreatedBy { get; set; }
[Required]
public DateTime ModifiedOn { get; set; }
[Required]
[StringLength(75)]
public string ModifiedBy { get; set; }
}
Thanks for the help!
The problem is that the Models.Provider class contains other classes like Models.Office, and Models.PhoneNumbers that were not eagerly loaded by the query. In addition to that, the Models.Provider class needs to be flattened. The Mapper wants to recursively map everything, and it keeps going down to the next class. For example, Provider.Office.Location.Offices.
The solution is to flatten Models.Provider and add .Include() to the query so it eagerly loads the data required.
I'll clean this up a bit more, but this is currently working.
public IEnumerable<Models.Provider> Get(string owner)
{
List<Data.Models.Provider> providers;
using (var db = new Data.ProviderDirectoryContext())
{
providers = db.Providers
.Where(p => p.Owner.Name == owner)
.Include("ProviderType")
.Include("ProviderSpecialty")
.Include("Office")
.Include("PhoneNumbers")
.ToList();
}
var dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
return dtoProviders;
}
public class Provider
{
public int Id { get; set; }
public int OwnerId { get; set; }
public int OfficeId { get; set; }
public string OfficeName { get; set; }
public int? ProviderTypeId { get; set; }
public string ProviderTypeName { get; set; }
public int? ProviderSpecialtyId { get; set; }
public string ProviderSpecialtyName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
public string Note { get; set; }
public DateTime? InactiveOn { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public string ModifiedBy { get; set; }
}
I am not sure how much this will help with performance but declaring the variable you don't want to dispose outside the using statement should fix your dispose exception.
public IEnumerable<Models.Provider> Get(string owner)
{
IEnumerable<Models.Provider> dtoProviders;
using (var db = new Data.ProviderDirectoryContext())
{
List<Data.Models.Provider> providers = db.Providers.Where(p => p.Owner.Name == owner).ToList();
dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
}
return dtoProviders;
}

One to many relation with cascade giving error

I am learning EF Code First with migrations, I have 3 entities :
[User] 1--->* [Call] 1--->* [ETA]
Code :
User.cs
public class User
{
[Key]
public int Id { get; set; }
public Guid LongId { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Call> Calls { get; set; } // many calls
public User()
{
LongId = Guid.NewGuid();
}
}
Call.cs
public class Call
{
[Key]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string BreakdownNo { get; private set; }
[Required,MaxLength(32)]
public string Customer { get; set; }
[Required,MaxLength(32)]
public string TrailerNo { get; set; }
[Required, MaxLength(32)]
public string DepotContact { get; set; }
[Required, MaxLength(48), RegularExpression(#"^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$")]
public string DepotEmail { get; set; }
[Required, MinLength(9), MaxLength(32)]
public string DepotPhone { get; set; }
[Required, MaxLength(32)]
public string DriverContact { get; set; }
[Required, MinLength(9), MaxLength(32), RegularExpression(#"^(7\d{3}|\(?07\d{3}\)?)\s?\d{3}\s?\d{3}$")]
public string DriverPhone { get; set; }
[Required, MaxLength(256)]
public string LocatedAtFreeText { get; set; }
[Required, MaxLength(8), RegularExpression(#"^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {0,1}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$")]
public string LocatedAtPostCode { get; set; }
[Required, MaxLength(16)]
public string StartupNo { get; set; }
[Required]
public bool IsLoaded { get; set; }
[Required, MaxLength(256)]
public string FaultDescription { get; set; }
[Required]
public DateTime StartTime { get; set; }
public DateTime? EndTime { get; set; }
public string Status { get; set; }
public virtual User Controller { get; set; } // 1 controller
public virtual ICollection<ETA> ETAs { get; set; } // many ETAs
public Call()
{
StartTime = DateTime.Now;
ETAs = new List<ETA> { new ETA() };
Status = "Logged";
}
}
ETA.c
public class ETA
{
[Key]
public int Id { get; set; }
[Required]
public TimeSpan Value { get; set; }
public int CallId { get; set; }
public ETA()
{
Value = TimeSpan.FromMinutes(90);
}
}
I would like it so when I delete the User it deletes all of the Calls for the User, which in turn deletes all of the ETAs for those Calls.
When I delete a User row from the Database (using database explorer) it gives me an error :
No rows were deleted.
A problem occurred attempting to delete row 201.
Error Source: .Net SqlClient Data Provider.
Error Message: The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Calls_dbo.Users_Controller_Id". The conflict occurred in database "BreakdownDb", table "dbo.Calls", column 'Controller_Id'.
You can turn on the Cascade Delete option in Entity Framework, here you will find more info:
http://blogs.msdn.com/b/alexj/archive/2009/08/19/tip-33-how-cascade-delete-really-works-in-ef.aspx
The solution was to add OnModelCreating method to my DbContext class :
public class BreakdownDb : DbContext
{
public DbSet<Call> Calls { get; set; }
public DbSet<User> Users { get; set; }
public BreakdownDb(): base("name=DefaultConnection") {}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Calls).WithRequired();
modelBuilder.Entity<Call>().HasMany(x => x.ETAs).WithRequired();
}
}