Validating uniqueness with data annotations in asp.net mvc - asp.net

I have various questions about validation using data annotations. I am using the following setup
asp.net mvc 2
entity framework 4
data annotations
Basically, I'm trying to get unique validation working and i'm a little confused. My models are as follows:
public class Buyer
{
public int Id { get; set; }
[Required(ErrorMessage = "The email is required")]
public string Email { get; set; }
[Required(ErrorMessage= "The name is required")]
public string Name { get; set; }
}
public class Seller
{
public int Id { get; set; }
[Required(ErrorMessage = "The email is required")]
public string Email { get; set; }
[Required(ErrorMessage= "The name is required")]
public string Name { get; set; }
}
I have set up a unique field attribute as follows
public class UniqueFieldAttribute : ValidationAttribute
{
public IUniqueValidator Validator { get; set; }
public int Id { get; set; }
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
return Validator.IsValid(Convert.ToString(value), Id);
}
}
I then created a validator that implements the IUniqueValidator interface:
public class BuyerUniqueEmailValidator : IUniqueValidator
{
public bool IsValid(string value, int id)
{
TheDb db = new TheDb();
var existing = from Buyer b in db.Buyers
where b.Email.ToLower() == value.ToLower()
select b;
foreach (Buyer b in existing)
{
if (b.Id != id)
{
return false;
}
}
return true;
}
}
The idea is there! However, on execution I am having problems. When I add this
[UniqueField(Validator=new BuyerUniqueEmailValidator(), Id=this.Id ErrorMessage= "This email is in use")]
the project won't compile.
Basically, what I want to know is if it is possible to pass a class to the validationAttribute to perform the validation? Also, how can i pass an id.
Additionally, is there anyway to create a generic unique field generator that would work for all my models that have an email field, or do I have to have a BuyerEmailValidator, a SellerEmailValidator etc, etc. I can't seem to get T working correctly.
Only worried about serverside at the moment.
Thanks

Your code implementation of this can't see any records not visible to its own transaction, and, hence, can't work reliably in a multi-user environment. Why not use a DB constraint?

Related

Troubleshooting model binding problem in ASP.NET Core 3.1 API

I'm trying to send an object via a POST request to my ASP.NET Core 3.1 API but I keep getting Bad Request error. As far as I can see, I do have a class that matches what I'm expecting perfectly but clearly it's not. How can I see exactly what the problem is?
The following fails:
public async Task<IActionResult> Post([FromBody] MyCustomObject input)
{
// Do something here...
}
If I use a dynamic, it works fine. So the following code works fine:
public async Task<IActionResult> Post([FromBody] dynamic input)
{
// Do something here...
}
As I said, I'm just getting a 400, Bad Request error. I've been going over MyCustomObject again and again and it looks identical to the object that I'm sending.
Here's what my custom class looks like:
public class CreateContactVm
{
[GuidEmptyNotAllowed]
public Guid AccountId { get; set; }
[Required]
public string AccountName { get; set; }
[GuidEmptyNotAllowed]
public Guid ContactGroupId { get; set; }
[IntZeroNotAllowed]
public int ContactType { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EntityName { get; set; }
public List<AddEmailVm> Emails { get; set; } = new List<AddEmailVm>();
public List<AddPhoneNumberVm> PhoneNumbers { get; set; } = new List<AddPhoneNumberVm>();
public List<AddAddressVm> Locations { get; set; } = new List<AddAddressVm>();
}
Here, I use some custom validations such as [GuidEmptyNotAllowed] or [IntZeroNotAllowed]. I inspect the object I send via my POST call and it satisfies ALL of these requirements and yet it still fails.
How can I get more information about why my API method is throwing a 400 error?
UPDATE:
The following code allows me to convert what comes in as a dynamic to my CreateContactVm custom class but I really shouldn't have to do this at all:
CreateContactVm request = new CreateContactVm();
try
{
var element = (JsonElement)input; // input is the dynamic received
request = JsonUtils.Deserialize<CreateContactVm>(element.GetRawText());
}
catch(Exception e)
{
var error = e.Message;
}
This also proves that the issue is with model binding. Something in my custom class is not liking the JSON object it receives.

ASP.NET MVC Auto generate integer number

Good day, a really newbie developer here.
I Have a form and it have a entity of "QueueNumber" Can someone show me how to code so that when ever i save my form it generates automatically QueueNumber + the Prefix, btw my prefix entity is in another class
public class Queue
{
public int QueueId { get; set; }
[Required]
public string Name { get; set; }
public string QueueNumber
public int ServiceId { get; set; }
public Service Service { get; set; }
}
-
public class Service
{
public int ServiceId { get; set; }
[Display(Name = "Service Name")]
public string ServiceName { get; set; }
[Display(Name = "Service Letter")]
public string ServiceLetter { get; set; }
[Display(Name = "Status")]
public bool? Status { get; set; }
[Display(Name = "Assigned Location")]
public int? LocationId { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<Customer> Customer { get; set; }
}
Outcome in database :
1. A001
2. A002
3. A003
i just want to be able to generate a queue number automatically and when i save in data base its like A= Service Letter and 001=QueueNumber. Thankyou
If the QueueNumber needs to be persisted to the table, then I would set it up as a calculated column so that the database can manage computing it and updating it if the underlying fields change.
If it is just something that you want to represent in the UI then I would recommend having the view model calculate this.
The entity can calculate something like this with a [NotMapped] attribute. For example:
public class Queue
{
public int QueueId { get; set; }
[Required]
public string Name { get; set; }
[NotMapped]
public string QueueNumber
{
get { return string.Format("{0}{1:000}", Service?.ServiceLetter ?? "?", QueueId);
}
[ForeignKey("Service")]
public int ServiceId { get; set; }
public Service Service { get; set; }
}
The problem with this approach is that to be able to rely on your Queue to reveal a QueueNumber, the Queue must eager load the Service, or you enable lazy loading and risk that performance hit vs. having Service == #null and getting an exception or invalid QueueNumber result. In the above example, if the Service isn't eager loaded you will get back something like "?001".
I prefer to use ViewModels for a number of reasons including performance, security, and handling conditions like this more cleanly.
For example, given a QueueViewModel as such:
[Serializable]
public sealed class QueueViewModel
{
public int QueueId{ get; set; }
public string Name { get; set; }
public string ServiceName { get; set; }
public string ServiceLetter { get; set; }
public string QueueNumber
{
return string.Format("{0}{1:000}", ServiceLetter, QueueId);
}
}
Then when reading the data, we don't pass Entities to the view, we pass our view model...
var viewModel = context.Queues
.Where(x => x.QueueId == queueId)
.Select(x => new QueueViewModel
{
QueueId = x.QueueId,
Name = x.Name,
ServiceName = x.Service.Name,
ServiceLetter = x.Service.ServiceLetter
}).Single();
return viewModel;
The benefits of this approach:
We don't have to worry about eager/lazy loading. The query fetches everything needed, and our view model can compute anything needed from the data loaded. (Queries can compute values as well if you like, but be wary of limitations in that the query has to be able to go to SQL, so no user functions, etc.)
Performance is improved since the query only returns the data needed rather than entire entity graphs, and no rish of lazy load hits.
Security is improved, we expose no more data to the client than is expected/needed, and we don't open the door for "lazy" updates where entities are attached to a context and saved without proper validation.

How to overload an Odata V2 controller method to accept multiple values as parameters?

I am new in ASP.NET MVC web development. I am just trying to overload a simple odata controller method for several days but failing again and again. I want to know the mystery behind this. Please help me...
This is my EducationInfo Model class...
public partial class EducationInfo
{
[Key]
public int EducationID { get; set; }
[ForeignKey("UserID")]
public int UserID { get; set; }
[Required]
public string Title { get; set; }
[Required]
[StringLength(50)]
public string EducationLevel { get; set; }
public string Department_Group { get; set; }
public string InstituteName { get; set; }
[Required]
public string Board_University_Name { get; set; }
[StringLength(30)]
public string Duration { get; set; }
public DateTime PassingYear { get; set; }
[StringLength(30)]
public string Result { get; set; }
public virtual UserInfo UserInfo { get; set; }
}
And here is one of my EducationInfoesController GET methods which accepts EducationID as parameter
// GET: odata/EducationInfoes(5)
[EnableQuery]
public SingleResult<EducationInfo> GetEducationInfo([FromODataUri] int key)
{
return SingleResult.Create(db.EducationInfoes.Where(educationInfo => educationInfo.EducationID == key));
}
I want to overload this method in a such way that it might take 2 parameters [e.g. GetEducationInfo(int UserID, string EducationLevel)] and return only a single result based on the combination of two parameters (UserID and EducationLevel).
I have already tried to overload this method by following code...
// GET: odata/EducationInfoes(5, "bachelor")
[EnableQuery]
public SingleResult<EducationInfo> GetEducationInfo([FromODataUri] int key, string eLevel)
{
return SingleResult.Create(db.EducationInfoes.Where(educationInfo => educationInfo.UserID == key && educationInfo.EducationLevel == eLevel));
}
But when I'm sending GET requst to my WebService by this URL http://localhost:10194/odata/EducationInfoes(5, "Bachelor"), I'm getting this message:
No HTTP resource was found that matches the request URI 'http://localhost:10194/odata/EducationInfoes(5,"Bachelor")'.
If I change the default method to the following...
// GET: odata/EducationInfoes(5)
[EnableQuery]
public SingleResult<EducationInfo> GetEducationInfo([FromODataUri] int key)
{
return SingleResult.Create(db.EducationInfoes.Where(educationInfo => educationInfo.UserId== key));
}
and requesting using this url http://localhost:10194/odata/EducationInfoes(3) and getting this message
The action 'GetEducationInfo' on controller 'EducationInfoes' returned a SingleResult containing more than one element. SingleResult must have zero or one elements.
this message is returned because every single user has multiple Educational Information stored in EducationInfo table.
But I must have to get every EducationInfo result separately or as single result based on UserID and EducationLevel but not based on EducationID. Please help me...

Conditionally validate collection/list item properties based on parent model value

I have the following view models
public class Step4ViewModel {
[Required(ErrorMessage="Yes/No Required")]
public bool? HaveVehicles { get; set; }
public List<Vehicle> Vehicles { get; set; }
public Step4ViewModel() {
this.Vehicles = new List<Vehicle>();
}
}
public class Vehicle {
public string Index { get; set; }
[DisplayName("Registration/Vin")]
[Required(ErrorMessage="Registration Required")]
[ValidVehicleRegistrationNumber(ErrorMessage = "Invalid Id Number")]
public string Registration { get; set; }
[Required(ErrorMessage="Make Required")]
public string Make { get; set; }
[Required(ErrorMessage="Model Required")]
public string Model { get; set; }
[Required(ErrorMessage="Year")]
public string Year { get; set; }
public Vehicle()
{
this.Index = Guid.NewGuid().ToString();
}
}
The validation works just fine. The problem is that it always works. I only want the validation to fire on vehicles in the collection when the user has indicated that they have vehicles. If HaveVehicles equals false I do not want the validation to fire.
Normally for this sort of validation I would build a custom validator in which I would only return validation errors if HaveVehicles is true. I am unable to do this here because HaveVehicles resides in a parent model which is inaccesible from the within the vehicle object.
One approach I've though of that may work is using a partial view to add/remove the collection from the DOM as null collections aren't validated. If the user selects yes ajax loads a partial view which contains a for loop that iterates over each vehicle and calls another partial view to display it.
Is there a better approach to do this or is something similar to the above my only option?

Why do the ASP.NET Identity interfaces use strings for primary and foreign keys?

I'm looking at the interfaces on the new ASP.NET Identity classes and the database it creates using Entity Framework Code First. I'm using the Visual Studio 2013 RC.
At first glance the database schema looks reasonably normal:
But all the keys are NVARCHAR(128)
And for some crazy reason AspNetUserSecrets.Id is a PK that looks like it could point to more than one record in the AspNetUsers table. Does this mean multiple AspNetUsers will have to share the same password?
When I look at the Looking at the interfaces you're forced to implement, these are all strings...
public class User : IUser
{
public string Id { get; set; }
public string UserName { get; set; }
}
public class UserSecret : IUserSecret
{
public string UserName { get; set; }
public string Secret { get; set; }
}
public class UserRole : IUserRole
{
public string UserId { get; set; }
public string RoleId { get; set; }
}
public class UserClaim : IUserClaim
{
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class UserManagement : IUserManagement
{
public string UserId { get; set; }
public bool DisableSignIn { get; set; }
public DateTime LastSignInTimeUtc { get; set; }
}
public class Tokens : IToken
{
public string Id { get; set; }
public string Value { get; set; }
public DateTime ValidUntilUtc { get; set; }
}
public class UserLogin : IUserLogin
{
public string UserId { get; set; }
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
public class Role : IRole
{
public string Id { get; set; }
public string Name { get; set; }
}
So I'm coming to terms with the fact that I may have to implement this using strings for PK and FK relationships.
But I'd really love to know WHY it's built like this...?
EDIT: Time has passed and there are now articles on how to extend the asp.net identity to use int (or guid) fields:
http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
The intent was to allow both arbitrary id types (i.e. int, guid, string), but also avoid having serialization/casting issues for the id property.
So you can define your keys however you like and just implement the interface method
public class MyUser : IUser {
public int Id { get; set; }
string IUser.Id { get { return Id.ToString(); } }
}
Adding to what Hao said:
The Identity runtime prefers strings for the user ID because we don’t want to be in the business of figuring out proper serialization of the user IDs (we use strings for claims as well for the same reason), e.g. all (or most) of the Identity interfaces refer to user ID as a string.
People that customize the persistence layer, e.g. the entity types, can choose whatever type they want for keys, but then they own providing us with a string representation of the keys.
By default we use the string representation of GUIDs for each new user, but that is just because it provides a very easy way for us to automatically generate unique IDs.
With ASP.NET Core, you have a very simple way to specify the data type you want for Identity's models.
First step, override identity classes from < string> to < data type you want> :
public class ApplicationUser : IdentityUser<Guid>
{
}
public class ApplicationRole : IdentityRole<Guid>
{
}
Declare your database context, using your classes and the data type you want :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
And in your startup class, declare the identity service using your models and declare the data type you want for the primary keys :
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders();
In ASP.NET identity tables, primary keys will still be in NVARCHAR but in your application it's will be the data type you want.
You can check this in a controller :
[HttpGet]
public async Task<IActionResult> Test()
{
ApplicationUser user = await _userManager.GetUserAsync(HttpContext.User);
Guid userId = user.Id; // No cast from string, it's a Guid data type
throw new NotImplementedException();
}

Resources