I have a model:
public class CoreGoal
{
[Key]
public long CoreGoalId { get; set; }
public string Title { get; set; }
public string Effect { get; set; }
public string Target_Audience { get; set; }
public string Infrastructure { get; set; }
public virtual ICollection<Benefit> Benefits { get; set; }
public virtual ICollection<Step> Steps { get; set; }
public virtual ICollection<Image> Images { get; set; }
public virtual ICollection<SubGoal> SubGoals { get; set; }
public CoreGoal()
{
}
}
And Image model is as following:
public class Image
{
[Key]
public long ImagelId { get; set; }
public byte[] Base64 { get; set; }
[ForeignKey("CoreGoalId")]
public long CoreGoalId { get; set; }
public Image()
{
}
}
My controller class:
[Route("api/[controller]")]
public class CoreGoalController : Controller
{
private readonly ICoreGoalRepository _coreGoalRepository;
//Controller
public CoreGoalController(ICoreGoalRepository coreGoalRepository) {
_coreGoalRepository = coreGoalRepository;
}
//Get methods
[HttpGet]
public IEnumerable<CoreGoal> GetAll()
{
return _coreGoalRepository.GetAllCoreGoals();
}
[HttpGet("{id}", Name = "GetCoreGoal")]
public IActionResult GetById(long id)
{
var item = _coreGoalRepository.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
//Create
[HttpPost]
public IActionResult Create([FromBody] CoreGoal item)
{
if (item == null)
{
return BadRequest();
}
_coreGoalRepository.CreateCoreGoal(item);
return CreatedAtRoute("GetCoreGoal", new { id = item.CoreGoalId }, item);
}
}
Repository:
public class CoreGoalRepository : ICoreGoalRepository
{
private readonly WebAPIDataContext _db;
public CoreGoalRepository(WebAPIDataContext db)
{
_db = db;
}
//Add new
public void CreateCoreGoal(CoreGoal coreGoal)
{
_db.CoreGoals.Add(coreGoal);
_db.SaveChanges();
}
//Get all
public IEnumerable<CoreGoal> GetAllCoreGoals()
{
return _db.CoreGoals
.Include(coreGoal => coreGoal.Benefits)
.Include(coreGoal => coreGoal.Steps)
.Include(coreGoal => coreGoal.Images)
.Include(coreGoal => coreGoal.SubGoals)
.ToList();
}
//Find specific
public CoreGoal Find(long key)
{
return _db.CoreGoals.FirstOrDefault(t => t.CoreGoalId == key);
}
}
public interface ICoreGoalRepository
{
void CreateCoreGoal(CoreGoal coreGoal);
IEnumerable<CoreGoal> GetAllCoreGoals();
CoreGoal Find(long key);
void DeleteCoreGoal(long id);
void UpdateCoreGoal(CoreGoal coreGoal);
}
When I do a POST request from swagger I get a template like:
{
"coreGoalId": 0,
"title": "string",
"effect": "string",
"target_Audience": "string",
"infrastructure": "string",
"benefits": [
{
"benefitId": 0,
"what": "string",
"coreGoalId": 0
}
],
"steps": [
{
"stepId": 0,
"what": "string",
"coreGoalId": 0
}
],
"images": [
{
"imagelId": 0,
"base64": "string",
"coreGoalId": 0
}
],
"subGoals": [
{
"subGoalId": 0,
"title": "string",
"priority": "string",
"audience": "string",
"social_aspects": "string",
"coreGoalId": 0,
"issues": [
{
"issueId": 0,
"title": "string",
"subGoalID": 0
}
]
}
]
}
If I POST like like this, my request fails with status 400, however if I remove
"images": [
{
"imagelId": 0,
"base64": "string",
"coreGoalId": 0
}
],
from this request, then it is successful. Why is it happening? All other models i.e. Benefit, Step are exactly identical to Image in structure.
UPDATE:
Changing base64 type from byte[] to string eliminates this problem but in that case while saving to my MySql database the big base64 string is chopped and kind of becomes useless to again form the image.
Related
Here is my code:
public class Element
{
public string? Group { get; set; }
public int Position { get; set; }
public string? Name { get; set; }
public int Number { get; set; }
[JsonPropertyName("small")]
public string? Sign { get; set; }
public double Molar { get; set; }
public IList<int>? Electrons { get; set; }
public override string ToString()
{
return $"{Sign} - {Name}";
}
}
public List<Element> LoadJson()
{
using (StreamReader r = new StreamReader("jsondata/elements.json"))
{
string json = r.ReadToEnd();
var ElementObject = JsonSerializer.Deserialize<List<Element>>(json);
return ElementObject;
}
}
elements.json
{
"elements": [
{
"group": "Other",
"position": 0,
"name": "Hydrogen",
"number": 1,
"small": "H",
"molar": 1.00794,
"electrons": [
1
]
},
{
"group": "Noble Gas (p)",
"position": 17,
"name": "Helium",
"number": 2,
"small": "He",
"molar": 4.002602,
"electrons": [
2
]
}
]
}
while I am trying to deserialize I get
System.Text.Json.JsonException: 'The JSON value could not be converted to System.Collections.Generic.List`1[BlazorApp4.Shared.Entities.Element]. Path: $ | LineNumber: 0 |
What am I missing?
The JSON doesn't represent a List<Element>. It represents an object which has a property which is a List<Element>.
Create that object:
public class Root
{
public List<Element> Elements { get; set; }
}
And deserialize into that:
var root = JsonSerializer.Deserialize<Root>(json);
return root.Elements;
I'm developing Web Api operations for User Controller and I don't understand why I'm getting the error in the POST method :
The "PK_Section" of the PRIMARY KEY constraint was violated. Unable to insert duplicate key into dbo.Section object. Duplicate key value: (3fa85f64-5717-4562-b3fc-2c963f66afa4).
My IdentityUser model:
public string MiddleName { get; set; }
[Required]
public string Surname { get; set; }
public DateTime BirthDate { get; set; }
public string Address { get; set; }
[Required]
public string UserType { get; set; }
public bool ParentsAgreement { get; set; }
public Section BelongSection { get; set; }
Section Model:
[Required]
public Guid Id { get; set; }
public string Name { get; set; }
public string CoachName { get; set; }
public string SportComplexTitle { get; set; }
[IgnoreDataMember]
public ICollection<User> UsersList { get; set; }
DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//section
modelBuilder.Entity<User>()
.HasOne(s => s.BelongSection)
.WithMany(a => a.UsersList);
modelBuilder.Entity<Section>()
.HasMany(s => s.UsersList)
.WithOne(a => a.BelongSection);
}
And my POST Method:
[HttpPost]
public async Task<ActionResult<User>> Add(User user)
{
try
{
if (user == null)
{
return BadRequest();
}
myDbContext.Users.Add(user);
await myDbContext.SaveChangesAsync();
var result = await myDbContext.Users.Include(o => o.BelongSection).FirstOrDefaultAsync(o
=> o.Id == user.Id);
return result;
}
catch (Exception)
{
throw;
}
}
So, I'm getting the error, that I can't use the Id of the existing already Section in creating User.
My Post method body is:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"userName": "string",
"normalizedUserName": "string",
"email": "string",
"normalizedEmail": "string",
"emailConfirmed": true,
"passwordHash": "string",
"securityStamp": "string",
"concurrencyStamp": "string",
"phoneNumber": "string",
"phoneNumberConfirmed": true,
"twoFactorEnabled": true,
"lockoutEnd": "2021-04-15T08:31:12.271Z",
"lockoutEnabled": true,
"accessFailedCount": 0,
"name": "string",
"middleName": "string",
"surname": "string",
"birthDate": "2021-04-15T08:31:12.271Z",
"address": "string",
"userType": "string",
"parentsAgreement": true,
"belongSection": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa4"
}
}
You could use two ways to add existing child to parent like below:
_context.Section.Attach(user.BelongSection);
Or:
_context.Entry(user.BelongSection).State = EntityState.Unchanged;
Be sure add AsNoTracking() to display the result.
Here is the whole working demo:
[HttpPost]
public async Task<ActionResult<User>> Add(User user)
{
try
{
if (user == null)
{
return BadRequest();
}
//_context.Section.Attach(user.BelongSection);
_context.Entry(user.BelongSection).State = EntityState.Unchanged;
_context.Users.Add(user);
await _context.SaveChangesAsync();
var result = await _context.Users.Include(o => o.BelongSection)
.AsNoTracking()
.FirstOrDefaultAsync(o=> o.Id == user.Id);
return result;
}
catch (Exception)
{
throw;
}
}
Note:
Not sure what is your asp.net core version, remember to add ReferenceLoopHandling.Ignore.
In asp.net core 3.x, System.Text.Json only supports serialization by value and throws an exception for circular references. So you need add Newtonsoft support like below:
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
In .Net 5.0,you could also add Newtonsoft support. Besides,System.Text.Json allows seting ReferenceHandler to Preserve:
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-preserve-references?pivots=dotnet-5-0
I have to publish a message using Python and consume it using MassTransit (.Net core app).
To test, I created a Console Application to produce the messages using the Azure.ServiceBuss library.
The consumer is a MassTransit implementation of the ServiceBus.
When I send the message to the queue (using my console application), MassTransit is not able to consume it.
I am aware of the Interoperability and all the headers we must send in order to MassTransit consume the messages, but I am not able to make the consumer work.
The producer:
namespace MassTransit.MultipleBus.ServiceBus.Models.Commands
{
class Program
{
const string ServiceBusConnectionString = "connstring";
const string QueueName = "topic";
static IQueueClient queueClient;
public static async Task Main(string[] args)
{
queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
// Send messages.
await SendMessagesAsync();
Console.WriteLine("ENVIOU");
Console.ReadKey();
await queueClient.CloseAsync();
}
private static async Task SendMessagesAsync()
{
try
{
while (true)
{
string messageBody = JsonConvert.SerializeObject(new EnviarExemploPocAzureServiceBusCommand(
NewId.NextGuid(),
NewId.NextGuid(),
new Proposta()
)
{
Headers = new Dictionary<string, object>() { },
Host = new HostInfo()
});
var message = new Message(Encoding.UTF8.GetBytes(messageBody));
Console.WriteLine($"Enviando mensagem: {messageBody}");
await queueClient.SendAsync(message);
Console.WriteLine("Enviado. Enviar outro?");
Console.ReadKey();
}
}
catch (Exception exception)
{
Console.WriteLine($"{DateTime.Now} :: Exception: {exception.Message}");
}
}
}
}
The class I'm sending:
namespace MassTransit.MultipleBus.ServiceBus.Models.Commands
{
public class EnviarExemploPocAzureServiceBusCommand
{
public EnviarExemploPocAzureServiceBusCommand(Guid msgId, Guid convId, Proposta msg)
{
MessageId = msgId.ToString();
ConversationId = convId.ToString();
MessageType = new[]
{
"urn:message:MassTransit.MultipleBus.ServiceBus.Models.Commands:Proposta"
};
Message = msg;
SourceAddress = "";
DestinationAddress = "sb://organization.servicebus.windows.net/topic";
}
public string DestinationAddress { get; set; }
public string MessageId { get; set; }
public string ConversationId { get; set; }
public string SourceAddress { get; set; }
public HostInfo Host { get; set; }
public IDictionary<string, object> Headers { get; set; }
public Proposta Message { get; set; }
public string[] MessageType { get; set; }
}
public class Proposta
{
public Proposta()
{
Nome = "TESTE 2";
AcaoId = 2;
Mensagem = "TESTE 2";
}
public string Nome { get; set; }
public int AcaoId { get; set; }
public string Mensagem { get; set; }
}
public class HostInfo
{
public HostInfo()
{
MachineName = "NOTEXXXXX";
ProcessName = "iisexpress";
ProcessId = 34188;
Assembly = "MassTransit.MultipleBus";
AssemblyVersion = "1.0.0.0";
FrameworkVersion = "3.1.5";
MassTransitVersion = "7.0.2.0";
OperatingSystemVersion = "Microsoft Windows NT 10.0.19041.0";
}
public string MachineName { get; set; }
public string ProcessName { get; set; }
public int ProcessId { get; set; }
public string Assembly { get; set; }
public string AssemblyVersion { get; set; }
public string FrameworkVersion { get; set; }
public string MassTransitVersion { get; set; }
public string OperatingSystemVersion { get; set; }
}
}
The message queued in ServiceBus:
{
"DestinationAddress": "sb://organization.servicebus.windows.net/topic",
"MessageId": "00010000-0faa-0009-6939-08d854e183aa",
"ConversationId": "00010000-0faa-0009-69f0-08d854e183aa",
"SourceAddress": "",
"Host": {
"MachineName": "NOTEXXXXX",
"ProcessName": "iisexpress",
"ProcessId": 32608,
"Assembly": "MassTransit.MultipleBus",
"AssemblyVersion": "1.0.0.0",
"FrameworkVersion": "3.1.5",
"MassTransitVersion": "7.0.2.0",
"OperatingSystemVersion": "Microsoft Windows NT 10.0.19041.0"
},
"Headers": {
},
"Message": {
"nome": "TESTE 2",
"acaoId": 2,
"mensagem": "TESTE 2"
},
"MessageType": [
"urn:message:MassTransit.MultipleBus.ServiceBus.Models.Commands:Proposta"
]
}
The consumer startup:
services.AddMassTransit<IAzureServiceBusService>(x =>
{
x.AddConsumer<EnviarExemploPocAzureServiceBusCommandHandler>();
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.UseConcurrencyLimit(serviceBusSettings.AzureServiceBusSettings.ExemploAplicacaoPocA.ConcurrencyLimit);
cfg.Host("connString", h =>
{
h.OperationTimeout = TimeSpan.FromSeconds(serviceBusSettings.AzureServiceBusSettings.ExemploAplicacaoPocA.OperationTimeout);
h.TransportType = Microsoft.Azure.ServiceBus.TransportType.AmqpWebSockets;
});
cfg.UseServiceBusMessageScheduler();
});
});
services.AddMassTransitHostedService();
services.AddSettings(configuration);
The handler:
namespace MassTransit.MultipleBus.ServiceBus.Handlers.CommandHandlers
{
public class EnviarExemploPocAzureServiceBusCommandHandler : IConsumer<Proposta>
{
public async Task Consume(ConsumeContext<Proposta> context)
{
await Task.CompletedTask;
}
}
public class EnviarExemploPocAzureServiceBusFaultCommandHandler : IConsumer<Fault<EnviarExemploPocAzureServiceBusCommand>>
{
public async Task Consume(ConsumeContext<Fault<EnviarExemploPocAzureServiceBusCommand>> context)
{
await Task.CompletedTask;
}
}
}
When I send the message using MassTransit as well, the consumer works and the message is queued like:
{
"messageId": "00010000-0faa-0009-6497-08d854edbfe1",
"conversationId": "00010000-0faa-0009-3b83-08d854edbfe5",
"sourceAddress": "sb://organization.servicebus.windows.net/XXXX",
"destinationAddress": "sb://organization.servicebus.windows.net/topic",
"messageType": [
"urn:message:MassTransit.MultipleBus.ServiceBus.Models.Commands:Proposta"
],
"message": {
"nome": "TESTE DOIS",
"acaoId": 2,
"mensagem": "TESTE DOIS"
},
"sentTime": "2020-09-09T18:25:37.6127127Z",
"headers": {
"MT-Activity-Id": "|1e87ff49-4beb56732bfd4887.3."
},
"host": {
"machineName": "NOTEXXXXXX",
"processName": "iisexpress",
"processId": 37092,
"assembly": "MassTransit.MultipleBus",
"assemblyVersion": "1.0.0.0",
"frameworkVersion": "3.1.5",
"massTransitVersion": "7.0.2.0",
"operatingSystemVersion": "Microsoft Windows NT 10.0.19041.0"
}
}
What am I doing wrong?
The namespace seems to be correct, I did everything I found about it and I can't make it work.
Can someone help me?
I am currently learning how to work with EF Core with a simple one to many setup, where a user can have many items. In terms of retrieving the data from the tables, this is fine with some DTO models; however, when I try and add a user with multiple items via Postman, I noticed that for each item it had duplicated the user that many times (i.e. a user with 3 items will create 3 items and 3 users):
Postman (POST)
{
"username": "xxx",
"dob": "xxx",
"location": "xxx",
"items":[{
"item": "xxx",
"category": "xxx",
"type": "xxx"
},
{
"item": "xxx",
"category": "xxx",
"type": "xxx"
},
{
"item": "xxx",
"category": "xxx",
"type": "xxx"
}]
}
Context:
namespace TestWebApplication.Database
{
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Items> Items { get; set; }
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
// erm, nothing here
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(i => i.Items)
.WithOne(u => u.User);
}
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext);
}
return base.SaveChanges();
}
}
}
Controller:
[HttpPost]
[Route("insertuseranditems")]
public ActionResult InsertUserAndItems(User user)
{
if (ModelState.IsValid)
{
using (MyContext myContext = _myContext as MyContext)
{
myContext?.Users?.Add(user);
int changes = myContext.SaveChanges();
if (changes > 0)
{
return Created("User saved", user);
}
}
}
return Accepted();
}
Items:
namespace TestWebApplication.Database
{
public class Items
{
[Key]
public int ItemId { get; set; }
public string Item { get; set; }
public string Category { get; set; }
public string Type { get; set; }
public virtual User User { get; set; }
}
}
Users:
namespace TestWebApplication.Database
{
public class User
{
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
public string Dob { get; set; }
public string Location { get; set; }
public ICollection<Items> Items { get; set; }
}
}
I revisited my code and changed my Items.cs model to:
namespace TestWebApplication.Database
{
public class Items
{
[Key]
public int ItemId { get; set; }
public string Item { get; set; }
public string Category { get; set; }
public string Type { get; set; }
[ForeignKey("User")] // added this
public int? UserId { get; set; } // added this
public virtual User User { get; set; }
}
}
Now, when I send the above json via Postman, I get multiple items with the same user as a Foreign Key.
The site below seemed to help:
The ForeignKey Attribute
I'm starting to work with Swagger using the Swashbuckle library for AspNetCore.
And when putting in my API an object with references it presents as if it were necessary to send all the fields of the references, when only the identifier (Id)
Here's an example:
Model Structure:
public class Cidade
{
public long Id { get; set; }
public string Nome { get; set; }
public Uf Uf { get; set; }
}
public class Uf
{
public long Id { get; set; }
public string Nome { get; set; }
public Pais Pais { get; set; }
}
public class Pais
{
public long Id { get; set; }
public string Nome { get; set; }
}
And the following API:
[Produces("application/json")]
[Route("api/Cidade")]
public class CidadeController : Controller
{
// POST: api/Cidade
[HttpPost]
public void Post([FromBody]Cidade value)
{
}
}
The result in Swagger is as follows:
And what I would like is the following (only up to uf.id):
How can I get this result?
I followed the logic of #HelderSepu answer, to get my solution, which would be as follows:
I built a Schema filter to add an example to the reference properties (Ref), which has a property called "Id":
public class ApplySchemaRefIdExtensions : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
if (schema.Properties != null)
{
foreach (var p in schema.Properties)
{
if (p.Value.Example == null && p.Value.Ref != null)
{
var reference = context.SystemType.GetProperty(p.Value.Ref.Split("/").LastOrDefault());
if (reference != null)
{
var id = reference.PropertyType.GetProperty("Id");
if (id != null)
{
p.Value.Example = new
{
Id = 123
};
p.Value.Ref = null;
}
}
}
}
}
}
}
On Startup.cs:
services.AddSwaggerGen(c =>
{
// ...
c.SchemaFilter<ApplySchemaRefIdExtensions>();
});
Result for the same example of the question:
I was looking on my samples and I think I found something you can use:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=P#/PolygonVolume/PolygonVolume_Post
On my case I'm adding more, you need less, but still what you need is just a custom example...
the JSON looks like this:
"PolygonVolumeInsideParameter": {
"properties": {
"Points": {
"items": {
"$ref": "#/definitions/Location"
},
"xml": {
"name": "Location",
"wrapped": true
},
"example": [
{
"Lat": 1.0,
"Lon": 2.0
},
{
"Lat": 5.0,
"Lon": 6.0
}
],
"type": "array"
},
"PlanId": {
"type": "string"
}
},
"xml": {
"name": "PolygonVolumeInsideParameter"
},
"type": "object"
},
And on swashbuckle I added the example it with an ISchemaFilter my code is here:
https://github.com/heldersepu/Swagger-Net-Test/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L891