I'm creating a new webapi pattern and i decided use a new pattern to make my consistency.
I'm trying to set my error messages on the constructor of my class object Pessoa
public class PessoaModel
{
public int PessoaId { get; set; }
public string PessoaNome { get; set; }
public string PessoaNomeFantasia { get; set; }
public string PessoaCnpjCpf { get; set; }
public string PessoaEmail { get; set; }
PessoaModel()
{
if (PessoaNome == null)
throw new Exception("Preencha Nome");
if (PessoaEmail == null)
throw new Exception("Preencha Email");
if (PessoaCnpjCpf == null)
throw new Exception("Preencha Cpf ou Cnpj");
}
}
Then the Exception happens but the Controllers continues running
[HttpPost]
[Route("Pessoa")]
public IHttpActionResult Post(PessoaModel pessoa)
{
if (_pessoa.Insert(pessoa))
return Ok();
return BadRequest("Pessoa não inserida");
}
Someone know how this work or if have a better way to do this?
What you are doing doesn't make sense. You are forcing properties to have a value, where you have done nothing to give them a value. At that time, they can only be set from the constructor.
Since you are using MVC/Web API, I would consider to use data annotations, to enforce the model to have the correct values.
public class PessoaModel
{
[Required(ErrorMessage = "ID is required.")]
public int PessoaId { get; set; }
}
In your action:
if (!this.ModelState.IsValid)
{
return RedirectToAction("Error"); // give an error, do something else
}
Related
My WebApi has a table for applications with the following class:
namespace Models.Public
{
[Index(nameof(UUID), nameof(UID), IsUnique = true)]
public class Application
{
public Application()
{
this.UUID = new Guid();
}
public int ID { get; set; }
public Guid UUID { get; set; }
[Required]
public string UID { get; set; }
public string Publisher { get; set; }
public string Name { get; set; }
public string Version { get; set; }
}
}
The field UUID and ID are unique, so I was able to generate the required HttpGet command to obtain the results matching for that.
However, I am trying to obtain an IEnumerable object of all the items that match the Publisher field. That is, return all object that have "Google" as their Publisher.
My attempts have not been successful and I am hoping for some advise to fix my code:
// GET: api/Application/<publisher>
[HttpGet("{publisher}")]
public async Task<ActionResult<IEnumerable<Application>>> GetApplication(string publisher)
{
var application = await _context.Application.ToListAsync(publisher);
if (application == null)
{
return NotFound();
}
return await _context.Application.ToListAsync();
}
Publisher is not a unique value, so I'd like to be able to return all items as a JSON object that have whatever Publisher I type in the list. If no matches, error handle with NotFound();.
You will need to filter using .Where, .Contains
// GET: api/Application/<publisher>
[HttpGet("{publisher}")]
public async Task<ActionResult<IEnumerable<ApplicationData>>> GetApplication(string publisher)
{
var applications = _context.Application.Where(a=>a.Publisher.Contains(publisher)));
/* You could also use == for exact match
var applications = _context.Application.Where(a=>a.Publisher == publisher));
*/
if (applications.Count() == 0)
{
return NotFound();
}
return await applications.ToListAsync();
}
I have data class using DataContract and implementing field validation using System.ComponentModel.DataAnnotations.
In my class, the type attribute renders some fields relevant while others are not applicable. That is, some fields, depending on the type attribute, will become invalid while others are valid.
I am trying to find a good pattern to validate such conditions in an elegant fashion. I am open to accepting that running into this situation might mean I need to break my class in a few chunks to accommodate such polymorphism. Although not sure how.
Just in case it is relevant, those data classes will be serialized and stored on ServiceFabric reliable collections. This validation if more from an API perspective.
As of today, I am performing the validation in this way (which I do not find satisfactory) in the endpoint controller.
if (voucher.ServiceType == ServiceType.VaccineCompany)
{
if (voucher.Asignee == null)
{
throw new ArgumentNullException("Asignee is required for company vaccine voucher");
}
if (!voucher.BookingTime.HasValue)
{
throw new ArgumentNullException("Booking time is required for company vaccine voucher");
}
if (!voucher.FixedPrice.HasValue)
{
throw new ArgumentNullException("Fixed price is required for company vaccine voucher");
}
if (voucher.Discount.HasValue)
{
throw new ArgumentNullException("Discount is not a valid argument for company vaccine type voucher");
}
}
if (voucher.ServiceType == ServiceType.Vaccine)
{
if (voucher.Asignee != null)
{
throw new ArgumentException("Invalid argument asignee");
}
if (voucher.BookingTime.HasValue)
{
throw new ArgumentNullException("Invalid argument booking time");
}
if (voucher.FixedPrice.HasValue && voucher.Discount.HasValue)
{
throw new ArgumentException("Fixed price and discount cannot be set simultaneously");
}
}
This is how the model looks like:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace Remoting.VoucherService
{
[DataContract]
public sealed class Voucher
{
[Required(ErrorMessage = "Code is required")]
[DataMember(Name = "code")]
public string Code { get; set; }
[Required(ErrorMessage = "Description is required")]
[DataMember(Name = "description")]
public string Description { get; set; }
[Required(ErrorMessage = "ServiceType is required")]
[DataMember(Name = "serviceType")]
public ServiceType ServiceType { get; set; }
[DataMember(Name = "discount")]
public double? Discount { get; set; }
[DataMember(Name = "fixedPrice")]
public double? FixedPrice { get; set; }
[DataMember(Name = "isValid")]
public bool Valid {
get { return TimesUsed < MaxUsage; }
set { }
}
[DataMember(Name = "maxUsage")]
public uint MaxUsage { get; set; } = 1;
[DataMember(Name = "asignee")]
public string Asignee;
[DataMember(Name = "bookingTime")]
public DateTime? BookingTime;
[DataMember(Name = "timesUsed")]
public uint TimesUsed { get; set; } = 0;
public void IncreaseUsage()
{
TimesUsed += 1;
}
private ExtensionDataObject data;
public ExtensionDataObject ExtensionData
{
get => data;
set => data = value;
}
}
}
I hope somebody can provide me with some insights!
I'm looking for alternative methods in C# to compare multiple variables to the same value, and I would optimally like for them to share the same subsequent instructions based on their conditional results.
For example, I have the following code:
string DOBResultsError = VerifySingleRecordReturned(DOBResults, APIParameters, 1);
string NameResultsError = VerifySingleRecordReturned(NameResults, APIParameters, 2);
if (DOBResultsError != string.Empty)
{
PatientRecordUpdate(DOBResults, APIParameters.PatientID, DOBResultsError);
}
else if (NameResultsError != string.Empty)
{
PatientRecordUpdate(NameResults, APIParameters.PatientID, NameResultsError);
}
I'm having to do explicitly instruct PatientRecordUpdate to be performed for each variable being compared to String.Null.
What I would like to have happen is something like the following:
if (DOBResultsError != string.Empty || NameResultsError != string.Empty)
{
//whichever isn't an empty string use to perform PatientRecordUpdate()
}
Is such syntax possible in C#?
Employing the switch keyword won't make a difference because even though I can have multiple circumstances resulting in the same instructions being performed, if I need to use the one of the comparison variables I would still need to explicitly state the code using the variable for each possible case.
string DOBResultsError = VerifySingleRecordReturned(DOBResults, APIParameters, 1);
string NameResultsError = VerifySingleRecordReturned(NameResults, APIParameters, 2);
string SSNResultsError = VerifySingleRecordReturned(SSNResults, APIParameters, 3);
string EmptyString = String.Empty;
switch (EmptyString)
{
case DOBResultsError:
case SSNResultsError: //can't use SSNResultsError with PatientRecordUpdate() without stating PatientRecordUpdate() again
PatientRecordUpdate(DOBResults, APIParameters.PatientID, DOBResultsError);
case NameResultsError:
PatientRecordUpdate(NameResults, APIParameters.PatientID, NameResultsError);
}
Any help appreciated.
UPDATE: Requested additional info
This is what VerifySingleRecordReturnedFrom() does. It checks a few conditions that would cause errors in the program and writes an error message to be added on the record in an SQL DB.
public static string VerifySingleRecordReturnedFrom(List<PatientList3> ReturnedPatientList, AutoPatientLookup APIParameters, int SearchCriteria = 0)
{
string ErrorMessage = String.Empty;
if (ReturnedPatientList.Count == 0)
{
//Error Message for Dob
if (SearchCriteria == 1)
{
ErrorMessage = string.Format("No patients were returned from for DOB ....");
return ErrorMessage;
}
//Error Message for Name
else if (SearchCriteria == 2)
{
ErrorMessage = string.Format("No patients were returned from for patient name ....");
return ErrorMessage;
}
//Error Message for PracticePatientNumber
else if (SearchCriteria == 3)
{
ErrorMessage = string.Format("No patients were returned from for PracticePatientNumber...");
return ErrorMessage;
}
}
// more than one patient in common results list from AttemptToMatchPatientsByDemographics() or results using PatientNumber
else if (ReturnedPatientList.Count() > 1)
{
switch(SearchCriteria)
{
case 1:
case 2:
ErrorMessage = String.Format("{0} number of patients were returned...");
break;
//More than one patient returned from for any given PracticePatientNumber
case 3:
ErrorMessage = String.Format("{0} number of patients were returned....");
break;
}
return ErrorMessage;
}
//No error in number of results from
return ErrorMessage;
}
All of the results(DOB/Name/SSN) types are List objects of the following PatientList3 object (I've included sub classes):
public class PatientList3
{
public Patient PatientNameID { get; set; }
public string PatientNumber { get; set; }
public string ChartNumber { get; set; }
public Gender2 Gender { get; set; }
public string DOB { get; set; }
public string PhoneNumber { get; set; }
public string SSN { get; set; }
}
public class Patient
{
public int ID { get; set; }
public PtName Name { get; set; }
}
public class PtName
{
public string First { get; set; }
public string Middle { get; set; }
public string Last { get; set; }
public string Suffix { get; set; }
public string Full { get; set; }
public string Preferred { get; set; }
}
public class Gender2
{
public string LookupType { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public int Order { get; set; }
public bool Active { get; set; }
public List<AlternateCodes> AlternateCodes { get; set; } //Not important, didn't include AlternativeCodes class
}
This is the class of APIParameters:
public class AutoPatientLookup
{
public string DOB { get; set; }
public string Gender { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int? PatientNumber { get; set; }
public string SSN { get; set; }
public int PracticeID { get; set; }
public int PatientID { get; set; }
}
Consider leveraging a base result type for some generic logic like this:
// assuming both results inherit from or implement some base type...
List<ResultBase> results = new List<ResultBase>()
{
DOBResults,
NameResults
}; // order matters since it's used for the parameter index
// Note that a class would be a much better option than a tuple here.
// Select will verify each result but only upon request until FirstOrDefault is fulfilled
Tuple<ResultBase, string> firstResultError = results
.Select((result, index) => new Tuple<ResultBase, string>(
result,
VerifySingleRecordReturned(result, APIParameters, index + 1)))
.FirstOrDefault(result => !string.IsNullOrEmpty(result.Item2 /* Error message */));
// If there was at least one error, call the original patient update record
// with the associated information.
if (firstResultError != null)
{
PatientRecordUpdate(
firstResultError.Item1 /* Failed result */,
APIParameters.PatientID,
firstResultError.Item2 /* Error message for that result */);
}
You'll want to use a new class instead of a tuple for maintainability reasons, but besides that this should get you started in a good direction.
[HttpGet("/api/notes/suggested")]
public JsonResult GetSuggestedNotes(string searchText)
{
//TODO: Podpowiedzi przy wpisywaniu tytułu
JsonResult result = null;
try {
List<Note> n = db.Notes.Include(x => x.NoteTags).ToList();
result = Json(n);
}
catch(Exception e)
{
Console.WriteLine(e);
}
return result;
}
public class Note
{
public Note()
{
CreationDate = DateTime.Now;
NoteTags = new HashSet<NoteTag>();
Parts = new HashSet<Part>();
}
public int ID { get; set; }
public virtual ICollection<NoteTag> NoteTags { get; set; }
public virtual ICollection<Part> Parts { get; set; }
public DateTime? CreationDate { get; set; }
[NotMapped]
public string TagsToAdd { get; set; }
[NotMapped]
public string TagsAsSingleString {
get
{
string result = "";
foreach(var nt in NoteTags)
{
result += nt.Tag.Name + " ";
}
return result;
}
}
}
public class NoteTag
{
public int NoteId { get; set; }
public virtual Note Note { get; set; }
public int TagId { get; set; }
public virtual Tag Tag { get; set; }
}
When I try to get data using this WebAPI controller, I get 502 bad gateway. No errors, everything's fine while debugging server. Data get from database correctly.
I suspect that it could be something similar to "infinite loop" but how to prevent it? (Note class is connected to collection of NoteTag objects that are connected back to Note which probably makes this loop).
And why there are no errors if something went wrong? :/
I don't know if it still relevant but i had the same problem and what worked for me it to Configure Newtonsoft.Json
SerializerSettings.ReferenceLoopHandling = ewtonsoft.Json.ReferenceLoopHandling.Ignore.
If you are using VS2015 MVC you can add the following code:
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
in the ConfigureServices method in the Startup class.
I think the problem its recursion, can you try with an Anonymous type
NoteTags has Note , imagine if the Note->NoteTags->Note->NoteTags->Note->NoteTags ...
`List n = db.Notes.Include(x => x.NoteTags).ToList();
var e = n.select(x=> new {property=value});
result = Json(e);`
Got a question. I get this error and I know it is due to the fact that int32 has a number limit of 2147483647. But I don't know why I am getting this error when the value in question (a telephone number of 11 digits) is defined as a string in our SQL database, a string in our web service and a string in our web application.
I assume it is something to do with the way the service serialises and deserialises data over a connection, but I was wanting to know if there is a way to force Number to use only the string instead of parsing it when deserialisation happens. Or even get it to parse as int64.
Here is the error exception. I removed the namespace and service name. It is the property Number that is causing the problem.
There was an error deserializing the object of type .".ClientPhone[]. The value '07721545554' cannot be parsed as the type 'Int32'."
And here is the code for the service and the service interface.
[DataContract]
public class ClientPhone
{
[DataMember]
public int? ClientNumberID { get; set; }
[DataMember]
public int? RefID { get; set; }
[DataMember]
public string Number { get; set; }
[DataMember]
public string NumberType { get; set; }
[DataMember]
public bool? PrimaryNumber { get; set; }
}
public partial class ClientNumberEntity
{
public int ClientNumbersID { get; set; }
public Nullable<int> RefID { get; set; }
public string ClientNumberType { get; set; }
public string ClientNumber { get; set; }
public Nullable<bool> PrimaryNumber { get; set; }
public virtual ClientDataEntity ClientData { get; set; }
}
public List<ClientPhone> GetClientsPhoneByReference(int _reference)
{
OurDatabaseEntities context = new OurDatabaseEntities();
var phoneEntity = (from c in context.ClientNumberEntities
where c.RefID == _reference
select c).ToList();
if (phoneEntity != null)
{
return TranslateClientPhoneEntityToPhoneNumberList(phoneEntity);
}
else
throw new Exception("Unable to get phone data");
}
private List<ClientPhone> TranslateClientPhoneEntityToPhoneNumberList(List<ClientNumberEntity> numberEntities)
{
List<ClientPhone> phoneList = new List<ClientPhone>();
foreach (ClientNumberEntity numberEntity in numberEntities)
{
ClientPhone phoneListMember = new ClientPhone();
phoneListMember.ClientNumberID = numberEntity.ClientNumbersID;
phoneListMember.RefID = numberEntity.RefID;
phoneListMember.Number = numberEntity.ClientNumber;
phoneListMember.NumberType = numberEntity.ClientNumberType;
phoneListMember.PrimaryNumber = numberEntity.PrimaryNumber;
phoneList.Add(phoneListMember);
}
return phoneList;
}
Any advice on a solution would be greatly appreciated! Thanks :)
Got a solution, albeit it's more stupidity on my end.
I didn't realise that my .EDMX entity diagram hadn't been updated with the new values from the database (I had to manually delete the entity and re-add it to force changes).
After re-compiling and updating the service reference, it worked.