System.ArgumentException: 'Id field is not correctly populated' - asp.net-core

I am trying to implement Redis, I am sending a put request from Postman and getting error.
Controller:
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.Created, Type = typeof(Brand))]
public async Task<IActionResult> Update([FromBody] Brand brand)
{
try
{
await _brandCollection.UpdateAsync(brand);
return Ok();
}
catch (Exception ex)
{
throw ex;
}
}
Error:
System.ArgumentException: 'Id field is not correctly populated'
From redis desktop manager I have confirmed that Id exists in record
Postman:
{
"Id": 1,
"Name": "Xiaomi",
"IsRegistered": "Yes",
"About": "Mobile Brand",
"Countries": [
"Pakistan",
"India",
"China"
]
}
Brand model class:
[Document(StorageType = StorageType.Json, Prefixes = new[] { "Brand" })]
public class Brand
{
[Indexed] public int Id { get; set; }
[Indexed] public string? Name { get; set; }
[Indexed] public string? IsRegistered { get; set; }
[Indexed] public string? About { get; set; }
[Indexed] public List<string>? Countries { get; set; }
}
I have injected HostedService as well in Program.cs:
builder.Services.AddHostedService<IndexCreationService>();
IndexCreationService:
public class IndexCreationService : IHostedService
{
private readonly RedisConnectionProvider _provider;
public IndexCreationService(RedisConnectionProvider provider)
{
_provider = provider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
await _provider.Connection.CreateIndexAsync(typeof(Person));
await _provider.Connection.CreateIndexAsync(typeof(Category));
await _provider.Connection.CreateIndexAsync(typeof(Brand));
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

Related

Invalid Include Path Error in Asp.net 6 Web API

After Adding a Include property on the parent model controller I am facing the following error:
'Microsoft.EntityFrameworkCore.Query.InvalidIncludePathError': Unable to find navigation ' CurrencyList' specified in string based include path ' CurrencyList'. This exception can be suppressed or logged by passing event ID 'CoreEventId.InvalidIncludePathError' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.'
I am sharing all the related class & model.
CurrencyList (Model) : This is the child class which I want to include into the parent class for populating the dropdown.
public class CurrencyList
{
[Key]
public int Id { get; set; }
[DisplayName("CURRENCY")]
[Required(ErrorMessage = "CURRENCY is a required field")]
public string? CurrencyName { get; set; }
[DisplayName("EXCHANGE RATE")]
[Required(ErrorMessage = "EXCHANGE RATE is a required field")]
public double Rate { get; set; }
}
CurrencyList Controller:
public class CurrencyListController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public CurrencyListController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpGet]
public IEnumerable<CurrencyList> Get()
{
IEnumerable<CurrencyList> objCurrencyDropDownList = _unitOfWork.Currency.GetAll();
return _unitOfWork.Currency.GetAll();
}
[HttpGet("{id:int}")]
public CurrencyList GetDetails(int id)
{
return _unitOfWork.Currency.GetDetails(id);
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult Create(CurrencyList obj)
{
if (ModelState.IsValid)
{
_unitOfWork.Currency.Add(obj);
_unitOfWork.Save();
return CreatedAtAction("GetDetails", new { id = obj.Id }, obj);
}
return BadRequest();
}
[HttpPut]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult Edit(CurrencyList obj)
{
if (ModelState.IsValid)
{
_unitOfWork.Currency.Update(obj);
_unitOfWork.Save();
return NoContent();
}
return BadRequest();
}
[HttpDelete]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult Delete(int? id)
{
var obj = _unitOfWork.Currency.GetFirstOrDefault(c => c.Id == id);
if (obj == null)
{
return NotFound();
}
_unitOfWork.Currency.Remove(obj);
_unitOfWork.Save();
return NoContent();
}
}
BTBNewLienOpening (Model) : This is the parent class where I created FK retalion with CurrencyList Model.
public class BTBNewLienOpening
{
[Key]
public int ExpLCNoId { get; set; }
[DisplayName("EXPORT L/C NO")]
[ValidateNever]
public string? ExpLCNo { get; set; }
[DisplayName("ORDER NO")]
[ValidateNever]
public string? OrderNo { get; set; }
[DisplayName("STYLE")]
public int? StyleListId { get; set; }
[ForeignKey("StyleListId")]
[ValidateNever]
public StyleList? StyleList { get; set; }
[DisplayName("VALUE")]
[ValidateNever]
[DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = false)]
public decimal? Value { get; set; }
[DisplayName("NET VALUE")]
[ValidateNever]
[DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = false)]
public decimal? NetValue { get; set; }
[DisplayName("CURRENCY")]
public int? CurrencyListId { get; set; }
[ForeignKey("CurrencyListId")]
[ValidateNever]
public CurrencyList? CurrencyList { get; set; }
[DisplayName("QTY PCS")]
[ValidateNever]
public string? QtyPcs { get; set; }
[DisplayName("SHIPMENT")]
[ValidateNever]
public string? Shipment { get; set; }
[DisplayName("EXPIRY DATE")]
[ValidateNever]
public string? Expiry { get; set; }
[DisplayName("COUNTRY")]
public int? CountryListId { get; set; }
[ForeignKey("CountryListId")]
[ValidateNever]
public CountryList? CountryList { get; set; }
}
BTBNewLienOpeningController : This is the controller where I use the include properties for including all the dropdown properties. In the GetAll() method I use the include properties and include the dropdown properties. If I run the program without CurrencyList the app works fine. But after adding CurrencyList it shows the Microsoft.EntityFrameworkCore.Query.InvalidIncludePathError'
public class BTBNewLienOpeningController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<BTBNewLienOpeningController> _logger;
public BTBNewLienOpeningController(IUnitOfWork unitOfWork, ILogger<BTBNewLienOpeningController> logger)
{
_unitOfWork = unitOfWork;
_logger = logger;
}
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult GetNewLienOpening()
{
try
{
IEnumerable<BTBNewLienOpening> objBTBNewLienOpeningList = _unitOfWork.BTBNewLienOpening.GetAll(includeProperties: "StyleList, CurrencyList, CountryList");
return Ok(objBTBNewLienOpeningList);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Something went wrong in the {nameof(GetNewLienOpening)}");
return StatusCode(500, "Internal Server Error, Please Try Again Leter!");
}
}
[HttpGet("{id:int}")]
public BTBNewLienOpening GetDetails(int id)
{
return _unitOfWork.BTBNewLienOpening.GetDetails(id);
}
//[Authorize(Roles = "Administrator")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult Create(BTBNewLienOpening obj)
{
if (ModelState.IsValid)
{
_unitOfWork.BTBNewLienOpening.Add(obj);
_unitOfWork.Save();
return CreatedAtAction("GetDetails", new { id = obj.ExpLCNoId }, obj);
}
_logger.LogError($"Something went wrong in the {nameof(Create)}");
return StatusCode(500, "Internal Server Error, Please Try Again Leter!");
}
[HttpPut]
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult Update(BTBNewLienOpening obj)
{
if (ModelState.IsValid)
{
_unitOfWork.BTBNewLienOpening.Update(obj);
_unitOfWork.Save();
return StatusCode(202, "Successfully Updated!");
}
_logger.LogError($"Something went wrong in the {nameof(Update)}");
return StatusCode(500, "Internal Server Error, Please Try Again Leter!");
}
[HttpDelete]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult Delete(int? id)
{
if (id < 1)
{
_logger.LogError($"Invalid Delete Attempt In {nameof(Delete)}");
return BadRequest();
}
try
{
var obj = _unitOfWork.BTBNewLienOpening.GetFirstOrDefault(c => c.ExpLCNoId == id);
if (obj == null)
{
_logger.LogError($"Invalid Delete Attempt In {nameof(Delete)}");
return BadRequest("Submitted Data Is Invalid");
}
_unitOfWork.BTBNewLienOpening.Remove(obj);
_unitOfWork.Save();
return NoContent();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Something Went Wrong In The {nameof(Delete)}");
return StatusCode(500, "Internal Server Error, Please Try Again Leter!");
}
}
}
Repository (Class) : Here I use the GetAll method and added the include functionality.
public class Repository<T> : IRepository<T> where T : class
{
private readonly ApplicationDbContext _db;
internal DbSet<T> dbSet;
public Repository(ApplicationDbContext db)
{
_db = db;
this.dbSet = _db.Set<T>();
}
public void Add(T entity)
{
dbSet.Add(entity);
}
public IEnumerable<T> GetAll(string? includeProperties = null)
{
IQueryable<T> query = dbSet;
if (includeProperties != null)
{
foreach (var includeProp in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProp);
}
}
return query.ToList();
}
public T GetFirstOrDefault(Expression<Func<T, bool>> filter, string? includeProperties = null)
{
IQueryable<T> query = dbSet;
query = query.Where(filter);
if (includeProperties != null)
{
foreach (var includeProp in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProp);
}
}
return query.FirstOrDefault();
}
public T GetDetails(int id)
{
return dbSet.Find(id);
}
public void Remove(T entity)
{
dbSet.Remove(entity);
}
public void RemoveRange(IEnumerable<T> entity)
{
dbSet.RemoveRange(entity);
}
public IEnumerable<T> GetProp(string? includeProperties = null)
{
IQueryable<T> query = dbSet;
if (includeProperties != null)
{
foreach (var includeProp in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProp);
}
}
return query.ToList();
}
}

How to properly deserialize asp.net core Model State errors to object in xamarin forms

I am having a silly problem trying to deserialize asp.net core model state errors to an object. My code is like this
For backend register method
[HttpPost("register-user")]
[ValidateModel]
public async Task<IActionResult> Index(RegisterDto registerDto)
{
try
{
Data.Models.User user = mapper.Map<RegisterDto, Data.Models.User>(registerDto);
user.LockoutEnd = DateTimeOffset.Now;
user.Warehouse = configuration["Config:Warehouse"];
user.SiteId = Convert.ToInt32(configuration["Config:SiteId"]);
IdentityResult result = await userManager.CreateAsync(user, registerDto.Password);
if (result.Succeeded)
{
AddLogInformation(logger, "User created a new account with password.");
string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token));
string confirmationUrl = Url.Action("Index", "EmailConfirmation",
new {userId = user.Id, code = token}, Request.Scheme);
emailSender.SendEmailAsync(registerDto.Email, "Confirm your email",
GetEmailConfirmationTemplate(registerDto.FirstName, registerDto.LastName,
confirmationUrl));
ApplicationRole retailPersonRole =
await roleManager.FindByNameAsync(RoleHelper.GetRetailUserRoleName());
if (retailPersonRole != null) await userManager.AddToRoleAsync(user, retailPersonRole.Name);
if (userManager.Options.SignIn.RequireConfirmedAccount)
{
AddLogInformation(logger, "Sent email confirmation email to user");
return Ok(SuccessResult(null));
}
//If confirm account is set to false
await signInManager.SignInAsync(user, false);
return Ok(SuccessResult(null));
}
// If we got this far, something failed, redisplay form
return Ok(FailedMessage(logger, "Cannot register user at this time. Please try again later."));
}
catch (Exception e)
{
return ServerErrorJsonResult(logger, "Error while trying to register user. Error message is: " + e.Message);
}
}
And I am catching the model state error in action filter and returning a response as below.
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var modelState = context.ModelState;
if (!modelState.IsValid)
context.Result = new BadRequestObjectResult(new JsonResult(new {modelError = true, Errors = modelState}));
}
}
Now in the front-end (xamarin), I have a model to where the error should be deserialised so that I can display a proper error to user.
My register model in the front end is like this
public class RegisterDto: BaseDto
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public AddressDto BillingAddress { get; set; }
public RegisterDto Errors { get; set; }
}
My Address Dto is like this
public class AddressDto
{
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string PostCode { get; set; }
public string State { get; set; }
}
I am creating the post request in xamarin like this.
public async Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "")
{
try
{
HttpClient httpClient = CreateHttpClient(token);
if (!string.IsNullOrEmpty(header))
{
AddHeaderParameter(httpClient, header);
}
var content = new StringContent(JsonConvert.SerializeObject(data));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, serializerSettings));
return result;
}
catch (Exception e)
{
return default;
}
}
And finally, in the view model, I am doing this
--
userToBeRegistered is an instance of RegisterDto
await something.PostAsync(UrlHelper.RegisterUrl, userToBeRegistered);
The serialized string output is like this
{
"errors":{
"Email":[
"Email is required"
],
"LastName":[
"Last name is required"
],
"Password":[
"Password is required"
],
"FirstName":[
"First name is required"
],
"PhoneNumber":[
"Phone number is required"
],
"BillingAddress.City":[
"Suburb is required"
],
"BillingAddress.State":[
"State is required"
],
"BillingAddress.Address1":[
"Street address is required"
],
"BillingAddress.PostCode":[
"Postcode is required"
]
},
"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title":"One or more validation errors occurred.",
"status":400,
"traceId":"|3c2d7d70-49a6eceecbeedab8."
}
My question is how can I deserialise it to an errors object. If I keep running the above code, then I get this error.
"Unexpected character encountered while parsing value: [. Path 'errors.Email', line 1, position 20."
Can anyone help me with this?
Try the below method.
Create ErrorInfor class:
class ErrorInfor
{
public MyError errors { get; set; }
public string type { get; set; }
public string title { get; set; }
public int status { get; set; }
public string traceId { get; set; }
public class MyError
{
public List<string> FirstName { get; set; }
public List<string> LastName { get; set; }
public List<string> PhoneNumber { get; set; }
public List<string> Email { get; set; }
public List<string> Password { get; set; }
[JsonProperty("BillingAddress.City")]
public List<string> Citiy { get; set; }
[JsonProperty("BillingAddress.State")]
public List<string> State { get; set; }
[JsonProperty("BillingAddress.Address1")]
public List<string> Address1 { get; set; }
[JsonProperty("BillingAddress.PostCode")]
public List<string> PostCode { get; set; }
}
}
then you could get the data from your above json string.
ErrorInfor errorInfor = JsonConvert.DeserializeObject<ErrorInfo>(json);

Masstransit not consuming non MassTransit messages

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?

ASP.Net core correct way of implementing http methods for related models

I have tow models Context and Connection as following:
public class Context
{
[Key]
public long ContextId { get; set; }
[Required]
public string Role { get; set; }
public ICollection<Connection> Connections { get; set; }
public Context()
{
}
}
And
public class Connection
{
[Key]
public long ConnectionId { get; set; }
[Required]
public string Name { get; set; }
public long ContextId { get; set; }
public Context Context { get; set; }
public Connection()
{
}
}
So far, I did not create any controller or repository for Connection. ContextRepositiry looks like following:
public class ContextRepository: IContextRepository
{
private readonly WebAPIDataContext _db;
public ContextRepository(WebAPIDataContext db)
{
_db = db;
}
public Context CreateContext(Context context)
{
_db.Contexts.Add(context);
_db.SaveChanges();
return context;
}
public void DeleteContext(long id)
{
Context context = GetContext(id);
if (context != null)
{
_db.Contexts.Remove(context);
_db.SaveChanges();
}
}
public List<Context> GetAllContexts()
{
return _db.Contexts.AsNoTracking().ToList();
}
public Context GetContext(long id)
{
return _db.Contexts.FirstOrDefault(o => o.ContextId == id);
}
public void UpdateContext(long id, Context context)
{
}
}
public interface IContextRepository
{
List<Context> GetAllContexts();
Context GetContext(long id);
Context CreateContext(Context context);
void UpdateContext(long id, Context context);
void DeleteContext(long id);
}
And it's controller:
[Route("api/[controller]")]
public class ContextController : Controller
{
private readonly IContextRepository _contexts;
public ContextController(IContextRepository contexts)
{
_contexts = contexts;
}
[HttpGet("")]
public IActionResult GetAllContexts()
{
try
{
List<Context> contexts = _contexts.GetAllContexts();
return Ok(contexts);
}
catch (EntityNotFoundException<Context>)
{
return NotFound();
}
}
[HttpGet("{id}")]
public IActionResult GetContext(long id)
{
Context context= _contexts.GetContext(id);
if (context == null)
{
return NotFound();
}
return Ok(context);
}
[HttpPost]
public IActionResult CreateContext([FromBody] Context context)
{
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
Context createdContext= _contexts.CreateContext(context);
if (createdContext== null)
{
return NotFound();
}
return CreatedAtAction(
nameof(GetContext), new { id = createdContext.ContextId}, createdContext);
}
[HttpPut("{id}")]
public IActionResult UpdateContext(long id, [FromBody] Context context)
{
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
try
{
_contexts.UpdateContext(id, context);
return Ok();
}
catch (EntityNotFoundException<Context>)
{
return NotFound();
}
}
[HttpDelete("{id}")]
public IActionResult DeleteCOntext(long id)
{
_contexts.DeleteContext(id);
return Ok();
}
}
Question: While creating a context I shouldn't have to enter any connection data i.e. it should be optional (look ta the swagger request bellow). However, on updating a specific context there could be connection data, and corresponding context should be updated accordingly.
Right now, in Swagger for POST if I enter something like:
{
"contextId": 0,
"role": "Employee",
"connections": [
{
"connectionId": 0,
"name": "",
"contextId": 0,
"context": {}
}
]
}
then it says, The Name field is required and The Role field is required (I am trying to send just context data like role and leaving blank connection data- which should be possible). If I remove "connections":[] then it posts with connections set to null, but don't want to remove it from there.

RavenDB UniqueConstraint doesn't seem to work

I've been trying for a day to get UniqueConstraint working, but it doesn't seem the are. I have a simple MVC6 site that creates a User on a POST. I'm expecting that on the second POST an exception should be thrown as a user will have already been created with the same properties. I'm wanting to ensure that the email address is unique.
using Raven.Client;
using Raven.Client.Document;
using Raven.Client.UniqueConstraints;
namespace MVC6Test.DomainModel
{
public class User
{
public string Id { get; private set; }
[UniqueConstraint]
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
}
}
namespace MVC6Test.Web.Controllers
{
public class AdminController : Microsoft.AspNet.Mvc.Controller
{
private IDocumentStore _documentStore { get; set; }
public IDocumentSession Session { get; set; }
[HttpPost]
[AllowAnonymous]
[Route("login")]
public async Task<IActionResult> Login(string userName, string password)
{
User user = new User() {
Email = "test#gmail.com"
};
Session.Store(user);
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (_documentStore.IsDefault()) {
_documentStore = context.HttpContext.RequestServices.GetRequiredService<IDocumentStore>();
}
Session = _documentStore.OpenSession();
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
using (Session) {
if (Session != null && context.Exception == null) {
Session.SaveChanges();
}
}
base.OnActionExecuted(context);
}
}
}
namespace MVC6Test.Web
{
public class Startup
{
private IDocumentStore DocumentStore;
public void ConfigureServices(IServiceCollection services)
{
DocumentStore = new DocumentStore {
DefaultDatabase = "MVC6Test",
Url = "http://localhost:3366"
};
DocumentStore.Listeners.RegisterListener(new UniqueConstraintsStoreListener());
DocumentStore.Initialize();
services.TryAddSingleton(typeof(IDocumentStore), (provider) => {
return DocumentStore;
});
}
public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime)
{
lifetime.ApplicationStopped.Register(() => {
DocumentStore.Dispose();
});
}
}
}
I do get this metadata on the items that are created:
{
"Raven-Entity-Name": "Users",
"Raven-Clr-Type": "MVC6Test.DomainModel.User, MVC6Test",
"Ensure-Unique-Constraints": [
{
"Name": "Email",
"CaseInsensitive": false
}
]
}