Hi I have an Invoice type like:
public class Invoice : IEntity, IValidatableObject
{
public virtual int Id { get; set; }
[Required(ErrorMessage = "Invoice Number is a required field.")]
[Display(Name = "Invoice Number:")]
public virtual string InvoiceNumber { get; set; }
[Required(ErrorMessage = "Invoice Date is a required field.")]
[Display(Name = "Invoice Date:")]
[DataType(DataType.Date)]
public DateTime? InvoiceDate { get; set; }
[Required(ErrorMessage = "Organisation is a required field.")]
[Display(Name = "Organisation:")]
public int OrganisationId { get; set; }
[Required(ErrorMessage = "Region is a required field.")]
[Display(Name = "Region:")]
public virtual int? AreaId { get; set; }
[Required(ErrorMessage = "Total (Exc. GST) is a required field.")]
[Display(Name = "Total (Exc. GST):")]
public decimal? TotalExcludingGst { get; set; }
[Required(ErrorMessage = "Total (Inc. GST) is a required field.")]
[Display(Name = "Total (Inc. GST):")]
public decimal? TotalIncludingGst { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual DateTime CreatedDateTime { get; set; }
public virtual string LastModifiedByUserName { get; set; }
public virtual DateTime? LastModifiedDateTime { get; set; }
// Navigation properties
public virtual Area Area { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual ICollection<InvoiceLine> InvoiceLines { get; set; }
#region IValidatableObject Members
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if ((TotalExcludingGst + (TotalExcludingGst * .15m)) != TotalIncludingGst) {
yield return new ValidationResult("The total (exc. Gst) + Gst does not equal the total (inc. Gst).");
}
}
#endregion
What I want to do is make sure on insert update that the combination of Organsation and InvoiceNumber is unique.
I'm considering something like:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var repository = new Repository<Invoice>();
if(!repositoy.CheckUnique(Id)) {
yield return new ValidationResult("The combination of Organisation and Invoice number is already in use");
}
}
Is this bad practise? To be instantiating the repository inside the model?
Is there a better way?
Your solution is not work correctly in a multi user scenario. Because between checking whether an ID exists and saving changes another record maybe inserted with that same ID.
You can create a Unique Constraint on your table. This is the safe way to ensure duplicates are not created.
Current versions of EF does not model/support Unique Constraints. However what you can do is catch the specific exception and check the error message. Then show the errors
try
{
//updation logic
context.SaveChanges();
}
catch (System.Data.DataException de)
{
Exception innerException = de;
while (innerException.InnerException != null)
{
innerException = innerException.InnerException;
}
if (innerException.Message.Contains("Unique_constraint_name"))
{
ModelState.AddModelError(string.Empty, "Error Message");
return;
}
ModelState.AddModelError(string.Empty, "Error Message");
return View();
}
If you are using ASP.NET Web forms you can check this answer
Related
Hi everyone I cant seem to figure out how to check a record before creating a new post in MVC
When a user creates a "SuperMember" I want it to check IF that "Character" has already been assigned that "membership" already aka a duplicate record. Each Character can have many memberships but not the same one.
Heres my code -
MODELS
public class Character
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CharacterID { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[Display(Name = "Hero Alias")]
public string HeroAlias { get; set; }
[Required]
[Display(Name = "Email Address")]
public string Email { get; set; }
public ICollection<SuperMembers> SuperMembers { get; set; }
}
public class Membership
{
[Key]
public int MembershipID { get; set; }
[Required]
[Display(Name = "Membership Name")]
public string MembershipName { get; set; }
[Required]
[Display(Name = "Membership Tyoe")]
public string Type { get; set; }
public ICollection<SuperMembers>SuperMembers { get; set; }
}
public class SuperMembers
{
[Key]
[Display(Name = "Membership Number")]
public int SuperMembersID { get; set; }
[Required]
[Display(Name = "Hero Alias")]
public int CharacterID { get; set; }
public Character Character { get; set; }
[Required]
[Display(Name = "Membership")]
public int MembershipID { get; set; }
public Membership Membership { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
[Required]
[Display(Name = "Account Balance")]
public decimal AccountBalance { get; set; }
}
Then the Controller POST Method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("SuperMembersID,CharacterID,MembershipID,AccountBalance")] SuperMembers superMembers)
{
if (ModelState.IsValid)
{
var IfAlreadyExists = dbo.SuperMembers.Where(x => x.CharacterID == CharacterID && x.MembershipID == MembershipName).FirstOrDefault();
if (IfAlreadyExists == null)
{
//POST
}
else
{
//Return ERROR "Sorry this Membership already exists"
}
_context.Add(superMembers);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["CharacterID"] = new SelectList(_context.Character, "CharacterID", "HeroAlias", superMembers.CharacterID);
ViewData["MembershipID"] = new SelectList(_context.Membership, "MembershipID", "MembershipName", superMembers.MembershipID);
return View(superMembers);
}
The dbo in "dbo.SuperMembers" is underlined too, im using a local db for this little practice project so not sure if thats also a factor?
Im fairly new to this, this is my first StackOverflow post so apologies if ive missed anything obvious - Would appreciate any pointers. Thanks
HAVING TROUBLE GETTING MY EVENTS CONTROLLER TO SHOW IN BROWSER. I AM RECEIVING ERROR MESSAGE THAT MY COLUMN NAME IS INVALID
SqlException: Invalid column name 'City'.
Invalid column name 'State'.
EVENT MODEL code
public class Events
{
[Key]
public int EventsId { get; set; }
[Required(ErrorMessage = "Title is requied")]
[StringLength(50, ErrorMessage = "Title cannot be more than 50 characters")]
public string EventTitle { get; set; }
[StringLength(150, ErrorMessage = "Description should not exceed 150 characters")]
public string EventDescription { get; set; }
[Required]
public DateTime EventStartTime { get; set; }
[Required(ErrorMessage = "Start Date cannot be in the past")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0;MMdd/yyyy}")]
public DateTime EventStartDate { get; set; }
[Required]
public DateTime EventEndTime { get; set; }
[Required(ErrorMessage = "End date cannot be less than Start date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0;MMdd/yyyy}")]
public DateTime EventEndDate { get; set; }
public string City { get; set; }
public string State { get; set; }
public string EventType { get; set; }
[Required]
public string OrganizerName { get; set; }
public string OrganizerContactInfo { get; set; }
[Required(ErrorMessage = " Max tickets cannot be 0")]
public int MaxTickets { get; set; }
[Required(ErrorMessage = "Avaliable tickets cannot be 0")]
public int AvaliableTickets { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
throw new NotImplementedException();
}
}
}
here
enter image description here
There is a lot of information missing. My guess would be the Events table in your database does not contain City or State columns.
New to MVC. When I try to add a user to the database using Entity Framework Database First I get this exception:
An exception of type 'System.Data.Entity.Validation.DbEntityValidationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
This is the code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(RegisterViewModel account)
{
if (ModelState.IsValid)
{
using (db)
{
bool duplicate = db.Users.Any(a => a.UserName == account.UserName);
if (duplicate)
{
ModelState.AddModelError("", "Username already exists in database!");
}
else
{
db.Users.Add(new StoreFront.Models.User { UserName = account.UserName, Password = account.Password, EmailAddress = account.EmailAddress, IsAdmin = false, DateCreated = DateTime.Now });
db.SaveChanges();
ModelState.Clear();
ModelState.AddModelError("RegisterSuccess", "Successfully registered!");
}
}
}
return View();
}
I have validation in my RegisterViewModel for all fields, and when I debug, IsValid = true, otherwise it wouldn't run anyway. Any help would be greatly appreciated...I have been struggling with this for a while.
P.S. Yes the password is currently being stored as a string, this is just a test project that won't be used in the real world.
EDIT: Added Models:
User Model from database:
public partial class User
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public User()
{
this.Addresses = new HashSet<Address>();
this.Orders = new HashSet<Order>();
this.ShoppingCarts = new HashSet<ShoppingCart>();
}
public int UserID { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string EmailAddress { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public Nullable<System.DateTime> DateCreated { get; set; }
public string CreatedBy { get; set; }
public Nullable<System.DateTime> DateModified { get; set; }
public string ModifiedBy { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Address> Addresses { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Order> Orders { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ShoppingCart> ShoppingCarts { get; set; }
}
Partial Model to add ConfirmPassword:
namespace StoreFront.Models
{
[MetadataType(typeof(RegisterViewModel))]
public partial class User
{
[DisplayName("Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords must match")]
public string ConfirmPassword { get; set; }
}
}
RegisterViewModel:
public class RegisterViewModel
{
public int UserID { get; set; }
[DisplayName("Username")]
[Required(ErrorMessage = "Username is required")]
public string UserName { get; set; }
[DisplayName("Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
[DisplayName("Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords must match")]
public string ConfirmPassword { get; set; }
[DisplayName("Email")]
[Required(ErrorMessage = "Email is required")]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$",
ErrorMessage = "Please enter a valid email")]
public string EmailAddress { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public Nullable<System.DateTime> DateCreated { get; set; }
}
Fix: When I looked up a tutorial about MVC it asked em to create a partial class and a meta class. That was for code first I believe, which basically made it a new field that my database didn't have a spot for, and I am using database first. So I removed the deleted the partial class for User and it stopped making ConfirmPassword an actual field in the database.
Don't know the real works of it, or if what I said makes sense, but I hope this helps someone eventually.
Remove
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$",
ErrorMessage = "Please enter a valid email")]
from RegisterViewModel
I am trying to create a new record using only some of the fields in my Domain Model. I have created a ViewModel for this and am using AutoMapper.
My code is failing at the minute due to the required fields that I have on my Domain Model. I have added opt=>opt.Ignore() on the necessary field however, I am still having problems.
When I remove [Required] from the StaffPresent field the record is added to the DB.
In my Global.asax.cs
Mapper.CreateMap<CustomerSupportRecord, CustomerSupportRecordForCreation>();
Mapper.CreateMap<CustomerSupportRecordForCreation, CustomerSupportRecord>().ForMember(p=>p.StaffPresent, opt=>opt.Ignore());
Domain Model
public class CustomerSupportRecord
{
public int CustomerSupportRecordID { get; set; }
[Required]
public int CustomerID { get; set; }
[Required]
public string EmployeeID { get; set; }
[Required(ErrorMessage = "Please enter a Date")]
[DataType(DataType.Date)]
[Display(Name = "Date")]
public DateTime Date { get; set; }
[Required(ErrorMessage = "Please select an Arrival Time")]
[DataType(DataType.Time)]
[Display(Name = "Arrival")]
public DateTime ArrivalTime { get; set; }
[DataType(DataType.Time)]
[Display(Name = "Departure")]
public DateTime? DepartureTime { get; set; }
[Required(ErrorMessage = "Please select a Type")]
[Display(Name = "Type")]
public int CustomerSupportTypeID { get; set; }
[Required(ErrorMessage = "Please enter the staff who were present at the Feedback")]
[Display(Name = "Staff Present at Feedback")]
public string StaffPresent { get; set; }
[Display(Name = "Setting")]
public string ReflectionSetting { get; set; }
[Display(Name = "Advisor")]
public string ReflectionAdvisor { get; set; }
[Display(Name = "Notes")]
public string Notes { get; set; }
[Display(Name = "Comments")]
public string Comments { get; set; }
// Navigation Properties
public virtual Customer Customer { get; set; }
public virtual CustomerSupportType CustomerSupportType { get; set; }
public virtual Employee Employee { get; set; }
}
ViewModel
public class CustomerSupportRecordForCreation
{
public int CustomerSupportRecordID { get; set; }
public int CustomerID { get; set; }
public string EmployeeID { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Date")]
public DateTime Date { get; set; }
[DataType(DataType.Time)]
[Display(Name = "Arrival")]
public DateTime ArrivalTime { get; set; }
[Display(Name = "Type")]
public int CustomerSupportTypeID { get; set; }
[Display(Name = "Notes")]
public string Notes { get; set; }
}
And finally my Controller
//
// GET: /CustomerSupport/CustomerSupportRecord/Create
public ActionResult Create()
{
ViewBag.CustomerSupportTypeID = new SelectList(db.CustomerSupportType, "CustomerSupportTypeID", "CustomerSupportTypeName");
var model = new CustomerSupportRecordForCreation { CustomerID = 1, EmployeeID = "20213" };
return View("Create", model);
}
//
// POST: /CustomerSupport/CustomerSupportRecord/Create
[HttpPost]
public ActionResult Create(CustomerSupportRecordForCreation customersupportrecord)
{
if (ModelState.IsValid)
{
var newRecord = Mapper.Map<CustomerSupportRecordForCreation, CustomerSupportRecord>(customersupportrecord);
db.CustomerSupportRecord.Add(newRecord);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CustomerSupportTypeID = new SelectList(db.CustomerSupportType, "CustomerSupportTypeID", "CustomerSupportTypeName", customersupportrecord.CustomerSupportTypeID);
return View(customersupportrecord);
}
AutoMapper's configuration doesn't have anything to do with validation attributes, it just specifies the rules for how to map objects between each-other.
In your case, the mapping:
Mapper.CreateMap<CustomerSupportRecordForCreation, CustomerSupportRecord>().ForMember(p=>p.StaffPresent, opt=>opt.Ignore());
tells AutoMapper not to copy the StaffPresent property.
If your database model has a [Required] attribute, you'll still need to set that data elsewhere.
One thing to note based on your comment, it's a good idea to add validation to your ViewModels, as it prevents you from calling your database methods with improper data, gives you client side validation, and lets you enforce different constraints than your model might require.
I'm trying to make use of the IValidatableObject as described here http://davidhayden.com/blog/dave/archive/2010/12/31/ASPNETMVC3ValidationIValidatableObject.aspx.
But it just wont fire when I'm trying to validate, the ModelState.IsValid is always true.
Here is my model code:
[MetadataType(typeof(RegistrationMetaData))]
public partial class Registration : DefaultModel
{
[Editable(false)]
[Display(Name = "Property one")]
public int PropertyOne { get; set; }
}
public class RegistrationMetaData :IValidatableObject
{
[Required(ErrorMessage = "Customer no. is required.")]
[Display(Name = "Customer no.")]
public string CustomerNo { get; set; }
[Display(Name = "Comments")]
public string Comments { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (new AccountModel().GetProfile(CustomerNo) == null)
yield return new ValidationResult("Customer no. is not valid.", new[] { "CustomerNo" });
}
}
I extend a LINQ to SQL table called Registration, my first guess was that this is not possible to do on a Meta class, but I'm not sure?
I do not get any errors, and it builds just fine, but the Validate method will not fire. What have I missed?
That's because it is the Registration model that should implement IValidatableObject and not RegistrationMetaData:
[MetadataType(typeof(RegistrationMetaData))]
public partial class Registration : IValidatableObject
{
[Editable(false)]
[Display(Name = "Property one")]
public int PropertyOne { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (new AccountModel().GetProfile(CustomerNo) == null)
yield return new ValidationResult("Customer no. is not valid.", new[] { "CustomerNo" });
}
}
public class RegistrationMetaData
{
[Required(ErrorMessage = "Customer no. is required.")]
[Display(Name = "Customer no.")]
public string CustomerNo { get; set; }
[Display(Name = "Comments")]
public string Comments { get; set; }
}