How to handle validation for Relationship PK and FK in MVC5 - asp.net

Hi I have 2 table name tblGroup and tblSubGroup and tblGroup has GroupId which is primary and tblSubGroup has Groupid which is foreign key.
Below are the model generated for them
tblGroup Model
public partial class tblGroup
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public tblGroup()
{
this.tblSubGroups = new HashSet<tblSubGroup>();
}
public int GroupID { get; set; }
[Required(ErrorMessage = "Group Name is Required")]
public string Title { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<tblSubGroup> tblSubGroups { get; set; }
}
tblSubGroup Model
public partial class tblSubGroup
{
public int SubGroupID { get; set; }
[Display(Name = "tblGroup")]
public int GroupID { get; set; }
[Required(ErrorMessage = "SubGroup Name is Required")]
public string Title { get; set; }
public virtual tblGroup tblGroup { get; set; }
}
Now on deleting record of From GroupTable it is giving issue. Instead I need to validate a message that "This record is bind with another table or entity. So it cannot be deleted". I need to show this kind of message.
As I am new I don't know this things is possible or not

Since you need to verify with the database you move this type of validation to the server.
[HttpPost]
public ActionResult Delete(Group group)
{
var grp = db.Group.FirstOrDefault(g => g.Id == group.Id);
if (HasSubGroups(grp))
{
ModelState.AddError("DeleteValidation", "Cannot delete while sub-groups exists");
return View(group);
}
// delete normally ...
}
Then you could display the errors on the view in several ways. The simplest is just to show the collection.
#Html.ValidationSummary()

Related

Saving data in asp.net core 3.1 Entity error, it adds fields

I have at issue at saving a model in the database.
I got the following Model:
[Table("data_details")]
public class DataDetail
{
[Key]
[Display(Name = "ID")]
[Column("id")]
public String Id { get; set; }
[Display(Name = "QUALIFICATION")]
[Column("qualification")]
public decimal? Qualification { get; set; }
[Display(Name = "DATA CONFIG ID")]
[Column("data_config_id")]
public String DataConfigId { get; set; }
public DataConfig DataConfig { get; set; }
}
In my dbcontext I have it like this:
modelBuilder.Entity<DataDetail>().HasOne(c => c.Dataconfig).WithMany(w => w.DataDetails).HasForeignKey(c => c.DataConfigId);
modelBuilder.Entity<DataDetail>().HasOne(c => c.Data).WithMany(w => w.DataDetails).HasForeignKey(c => c.DataId);
When Saving
_context.Add(dataDetail);
_context.SaveChanges();
It tries to save but in the log I get an error , like it's trying to add an extra field
INSERT INTO `data_details` (`id`, `qualification`, `data_config_id`, `DataId`)
VALUES (#p0, #p1, #p2, #p3);
Microsoft.EntityFrameworkCore.Update[10000]
An exception occurred in the database while saving changes for context type 'Systema.Data.SystemaContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Unknown column 'DataId' in 'field list'
DataId Is not specified in this table at all :S
In your dbcontext,you have created a many-to-many relationship between Dataconfig and Data.EFCore will create DataConfigId and DataId as foreign keys of DataDetail by default.
Add DataDetail means to create a relationship between Dataconfig and Data. The DataId and DataconfigId you add must be existing entities, otherwise an error will be reported.
Your code should be:
public class Data
{
public string Id { get; set; }
public string Name { get; set; }
public IEnumerable<DataDetail> DataDetails { get; set; }
}
public class DataDetail
{
[Key]
[Display(Name = "ID")]
[Column("id")]
public String Id { get; set; }
[Display(Name = "QUALIFICATION")]
[Column("qualification")]
public decimal? Qualification { get; set; }
[Display(Name = "DATA CONFIG ID")]
[Column("data_config_id")]
public string DataConfigId { get; set; }
public DataConfig DataConfig { get; set; }
[Display(Name = "DATA Data ID")]
[Column("data_id")]
public string DataId { get; set; }
public Data Data { get; set; }
}
public class DataConfig
{
public string Id { get; set; }
public string Name { get; set; }
public IEnumerable<DataDetail> DataDetails {get;set;}
}
About many to many releationship.

How to dynamically add rows to my table and postback the data to view

As the question suggests I am using ENTITY FRAMEWORK in my ASP.NET application in which I have a table which the user can add rows to and input data into it. I need to know how I can post this data back to controller.
My models : (There were generated by EF when i used database first approach)
public partial class TRA_FORMS
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public TRA_FORMS()
{
this.TRA_ITEMS = new HashSet<TRA_ITEMS>();
}
public int ID { get; set; }
[Display(Name = "Name of TRA")]
public string NAME_OF_TRA { get; set; }
[Display(Name = "Requested amount")]
public Nullable<double> AMOUNT_REQUESTED { get; set; }
[Display(Name = "Name of commitee member making application")]
public string COMMITEE_MEMBER_NAME { get; set; }
[Display(Name = "Date of last AGM")]
public Nullable<System.DateTime> DATE_OF_AGM { get; set; }
[Display(Name = "Signed")]
public string SIGNED { get; set; }
[Display(Name = "Dated")]
public Nullable<System.DateTime> DATED { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<TRA_ITEMS> TRA_ITEMS { get; set; }
}
public partial class TRA_ITEMS
{
public int ITEM_ID { get; set; }
[Display(Name = "Description of Items")]
public string DESC_OF_ITEMS { get; set; }
[Display(Name = "Quantity")]
public Nullable<int> QUANTITY { get; set; }
[Display(Name = "Cost")]
public Nullable<double> COST { get; set; }
[Display(Name = "From ID")]
public Nullable<int> FORM_ID { get; set; }
public virtual TRA_FORMS TRA_FORMS { get; set; }
}
My controller:
[HttpPost]
public ActionResult Index(TRA_FORMS traForms)
{
return View();
}
I'm not showing my view here as It's wrong and I dont know how to go about it. But the view accepts a model of type TRA_FORMS. I have a table in this view (regular html ) which has 3 columns - each for properties from the TRA_ITEMS model. The Desc of items, quantity and cost. Users can add any number of items to this table. Ideally on postback it should postback a List which holds each item added by the user but i don't know how to do this. I've looked at several Stack overflow questions related to this and been looking for solutions the whole day but I'm a newbie to ASP.NET so having trouble applying most answers i found to my scenario.

ASP.NET Core Conflict with Foreign Key

I have got several models:
Course.cs
public class Course
{
public Guid Id { get; set; }
public ICollection<ApplicationUser> Teacher { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public DateTime CreationDate { get; set; }
public bool IsActive { get; set; }
}
Group.cs
public class Group
{
public Guid Id { get; set; }
public ApplicationUser Mentor { get; set;}
public string DisplayName { get; set; }
public string GroupName { get; set; }
public DateTime StartYear { get; set; }
public string InviteCode { get; set; }
public ICollection<ApplicationUser> Students { get; set; }
public ICollection<Course> Courses { get; set; }
}
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
[Required]
public string Firstname { get; set; }
[Required]
public string Surname { get; set; }
public bool Gender { get; set; }
public DateTime Birthdate { get; set; }
//[Required]
public string InviteCode { get; set; }
public Guid GroupId { get; set; }
[ForeignKey("GroupId")]
public Group CurrentGroup { get; set; }
public ICollection<Group> PastGroups { get; set; }
}
Now when I try to register (using Identity) a user (not even trying to give the user a group) I receive this error:
SqlException: The INSERT statement conflicted with the FOREIGN KEY
constraint "FK_AspNetUsers_Groups_GroupId". The conflict occurred in
database "aspnet-Project_Dojo-3af15f80-8c62-40a6-9850-ee7a296d0726",
table "dbo.Groups", column 'Id'. The statement has been terminated.
In my modelBuilder I have added some logics for the relations between Group, ApplicationUser (Students) and the Foreign Key:
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);\\
builder.Entity<ApplicationUser>()
.HasOne(p => p.CurrentGroup)
.WithMany(b => b.Students)
.HasForeignKey(p => p.GroupId);
}
I don't know what this is exactly doing, but I've been browsing some Stackoverflow threads to come to this code (migrations weren't working without it).
I look forward to a solution for my problem. Once again, I'm not doing ANYTHING with the groups yet when registering.
Thanks in advance!
not even trying to give the user a group
Well there's your problem, it's required.
Either provide a group, or make it optional by making the foreign key nullable (Guid? GroupId).
Because it's currently a non-nullable struct, it'll have a default value of all zeroes (Guid.Empty). This FK is not known in your database, resulting in the error you see.

ModelState is false for relations with required fields

I have two models Invoice and Client which has one to many relation. My problem is when I try to create a new invoice the modelstate becomes false because the required fields of the Client object are not filled. I tried to set default values e.g. int property {get; set;} = 1; but this causes to override the property value when I try to retrieve the relational model. I'm new to asp.net any help would be appreciated.
Here is my Client model code:
public class Client
{
public int ClientId { get; set; }//Primary key
[Required]
[Display(Name = "Client/Company Name")]
public string Name { get; set; }
//.....
public List<Invoice> Invoices { get; set; }
}
Invoice Model:
public class Invoice
{
public int InvoiceId { get; set; }//Primary key
[Required]
[Display(Name = "Client/Company Name")]
public int ClientId { get; set; }//Foreign key
public Client client { get; set; }
//....
}
My code to save an invoice:
[HttpPost]//Save quote & redirect to edit quote
public IActionResult Create(Invoice quote)
{
ViewData["Title"] = "Create Quotation";//Set title of the view!
if (ModelState.IsValid)//If quote is validated to true.
{
_context.Invoices.Add(quote);//insert quote
_context.SaveChanges();
//Redirect to edit page to add items to the invoice
return RedirectToAction("Edit", new { id = quote.InvoiceId });
}
//...
}
My code to retrieve invoices with their client names
public IActionResult Index()
{
ViewData["Title"] = "Quotations";//Set title of the view!
//Return with array of all quotations.
return View(_context.Invoices.Include(i => i.client).Where(i => i.isQuote == true).ToArray());
}
Any hints or help would be appreciated.
Found a simpler solution:
Just add virtual keyword and GG
in Invoice Model:
public class Invoice
{
public int InvoiceId { get; set; }//Primary key
[Required]
[Display(Name = "Client/Company Name")]
public int ClientId { get; set; }//Foreign key
public virtual Client client { get; set; }
//....
}

EF 5, one to many, more than one table

I'm having a little of trouble with the following classes:
public class TwoVariableDetails
{
public TwoVariableDetails()
{
MovementsPerBlocks = new HashSet<MovementsRow>();
MovementsPerShiftTypes = new HashSet<MovementsRow>();
MovementsPerMachines = new HashSet<MovementsRow>();
MovementsPerShifts = new HashSet<MovementsRow>();
}
[Key]
public Guid TwoVariableDetailsId { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[Required]
[MaxLength(1000)]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
public virtual ICollection<MovementsRow> MovementsPerBlocks { get; set; }
public virtual ICollection<MovementsRow> MovementsPerShiftTypes { get; set; }
public virtual ICollection<MovementsRow> MovementsPerMachines { get; set; }
public virtual ICollection<MovementsRow> MovementsPerShifts { get; set; }
}
[Table("Movement")]
public class MovementsRow
{
public MovementsRow()
{
MovementsCells = new HashSet<MovementsCell>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[CsvField(Ignore = true)]
public Guid MovementId { get; set; }
[Required]
public int RowNo { get; set; }
[Required]
[CsvField(Ignore = true)]
public Guid ModelId { get; set; }
[ForeignKey("ModelId")]
[CsvField(Ignore = true)]
public virtual TwoVariableDetails Model { get; set; }
[TypeConverter(typeof(MovementsCellTypeConverter))]
public virtual ICollection<MovementsCell> MovementsCells { get; set; }
}
[Table("MovementCell")]
public class MovementsCell
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[CsvField(Ignore = true)]
public Guid CellId { get; set; }
[Required]
public int ColumnNo { get; set; }
[Required]
public int Count { get; set; }
[Required]
[CsvField(Ignore = true)]
public Guid MovementId { get; set; }
[ForeignKey("MovementId")]
[CsvField(Ignore = true)]
public virtual MovementsRow Model { get; set; }
}
When I try to save it to the database I get the following error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Movement_dbo.TwoVariableDetails_ModelId". The conflict occurred in database "aspnet-GreenCranes.UI-20130516", table "dbo.TwoVariableDetails", column 'TwoVariableDetailsId'.
The statement has been terminated.
This is the code I'm using for saving:
twoVariableDetails.TwoVariableDetailsId = Guid.NewGuid();
_context.TwoVariableDetailsModels.Add(twoVariableDetails);
_context.SaveChanges();
My table looks like this:
Movement
- Column
- MovementId
- RowNo
- ModelId(FK, uniqueidentifier, not null)
- TwoVariableDetails_TwoVariableDetailsId(FK, uniqueidentifier, null)
- TwoVariableDetails_TwoVariableDetailsId2(FK, uniqueidentifier, null)
- TwoVariableDetails_TwoVariableDetailsId3(FK, uniqueidentifier, null)
- TwoVariableDetails_TwoVariableDetailsId4(FK, uniqueidentifier, null)
- Keys
- FK_dbo.Movement_dbo.TwoVariableDetails_ModelId
- FK_dbo.Movement_dbo.TwoVariableDetails_TwoVariableDetails_TwoVariableDetailsId
- FK_dbo.Movement_dbo.TwoVariableDetails_TwoVariableDetails_TwoVariableDetailsId1
- FK_dbo.Movement_dbo.TwoVariableDetails_TwoVariableDetails_TwoVariableDetailsId2
- FK_dbo.Movement_dbo.TwoVariableDetails_TwoVariableDetails_TwoVariableDetailsId3
I'm not sure what is the problem with my approach. Should I change the MovementsRow class to have four Model properties and four modelid fk and then use InverseProperty attribute?
MovementsRow.Model belongs to another relationship than the four collections in TwoVariableDetails. That's the reason why you don't have four, but five foreign keys in the database table. When you insert twoVariableDetails into the DB and it contains a MovementRow instance in one of the collections EF expects that its ModelId is set to a Guid that references an existing TwoVariableDetails row - which it doesn't apparently. Hence the exception.
Should I change the MovementsRow class to have four Model properties
and four modelid fk and then use InverseProperty attribute?
I'd say yes. It's probably the best solution. The alternative is to have no Model property at all in MovementRow. It's working but you would not be able to navigate from MovementRow to TwoVariableDetails then.
Your FK_dbo.Movement_dbo.TwoVariableDetails_ModelId is being violated, simply put - the ModelId that the Movement record is using doesn't yet exist in TwoVariableDetails.
If you wanted to keep it simple, and transactional, then you could use TransactionScope along with your database context, save the TwoVariableDetails first in the transaction, and then the records that relate back to it:
using (var context = new MyDbContext())
using (var tranScope = new TransactionScope(TransactionScopeOption.Required) {
// don't save the Movement records yet
twoVariableDetails.TwoVariableDetailsId = Guid.NewGuid();
_context.TwoVariableDetailsModels.Add(twoVariableDetails);
_context.SaveChanges();
// now create the movement records, add them to twoVariableDetails
...
_context.SaveChanges();
// commit the transaction
scope.Complete();
}

Resources