Update composite foreign key - asp.net-mvc-3

I have a self referencing model with composite primary key in ASP.NET MVC 3 using code first approach:
public class Area
{
[Key, Column(Order=0)]
public int Id1 { get; set; }
[Key, Column(Order=1)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual Area Parent { get; set; }
}
And I would like to have a controller with create and edit operations that can work with all properties, including the composite Parent (that must be previously added to the database).
I managed to get the create method running, but for editing the complex field doesn't want to update. The input data for the following method successfully parses to the object area that also has area.Parent.Id1 and area.Parent.Id2 set.
Current code that doesn't save changes for modifications to Parent:
[HttpPost]
public ActionResult Edit(Area area)
{
try
{
if (ModelState.IsValid)
{
if (area.Parent != null)
{
area.Parent = db.Areas.Find(area.Parent.Id1, area.Parent.Id2);
if (area.Parent == null)
throw new NotFoundException();
// need to mark it as modified...
}
db.Entry(area).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (NotFoundException)
{
//...
}
return View(area);
}

I found a kind of workaround if anyone needs it. Add this to the model:
[ForeignKey("Parent"), Column(Order = 0)]
public int Parent_Id1 { get; set; }
[ForeignKey("Parent"), Column(Order = 1)]
public int Parent_Id2 { get; set; }
And add this where it says "need to mark it as modified":
area.Parent_Id1 = area.Parent.Id1;
area.Parent_Id2 = area.Parent.Id2;

Related

Creating an object with other object properties from DbContext in MVC EF Core

I am sorry if the answer to this question is so obvious, but all (and I really mean all) of my Google search results are purple, regardless of the search phrase I try to wrap around my question.
I am trying to build an MVC .NET Core 3.0 application with Code-First.
I am able to create my models, have these setup correct (I think) in my Database (Azure SQL), and using Visual Studio's standard templates to create controllers and views. I am therefore able to create each one of my model individually.
What I am trying to do is
Create a view from where I can create a Rental-object, with link to RentalOwner and ParkingSpot.
(Create a view from where I can see all rentals created. This is not in scope for this question)
My models
public class ParkingSpot
{
public int ParkingSpotId { get; set; }
public int ParkingSpotNumber { get; set; }
}
public class RentalOwner
{
public int RentalOwnerId { get; set; }
public int TenantId { get; set; }
public Tenant Tenant { get; set; }
}
public class Tenant
{
public int TenantId { get; set; }
[Required]
[Display(Name="Navn")]
public string Name { get; set; }
[EmailAddress]
[Required]
public string Email { get; set; }
}
public class Rental
{
public int RentalId { get; set; }
public int ParkingSpotId { get; set; }
public ParkingSpot ParkingSpot { get; set; }
public int RentalOwnerId { get; set; }
public RentalOwner RentalOwner { get; set; }
}
I have tried creating a ViewModel, to use for creation of the Rental-model and the binding to other models.
public class RentalCreationView
{
public int Id { get; set; }
public Rental Rental { get; set; }
public int ParkingSpotId { get; set; }
public int TenantId { get; set; }
}
I've tried with this GET and HttpPost POST Actions in my Controller.
// GET: Rentals/Create
public async Task<IActionResult> CreateAsync()
{
var parkingSpots = await _context.ParkingSpots.ToListAsync();
ViewData["ParkingSpots"] = new SelectList(parkingSpots, "ParkingSpotId", "ParkingSpotNumber");
var tenants = await _context.Tenants.ToListAsync();
ViewData["Tenants"] = new SelectList(tenants, "TenantId", "Name");
return View();
}
public async Task<IActionResult> Create(RentalCreationView rcw)
{
if (ModelState.IsValid)
{
_context.RentalOwners.Add(rcw.Rental.RentalOwner);
_context.Rentals.Add(rcw.Rental);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(rcw);
}
I don't think my issue is with my View, and I guess I am missing something in either my models or in my controller.
Right now, when I try to create a Rental, the view is just loading for a couple of minutes after submit.
I have seen numerous of tutorials, read hundreds of questions and articles, and I know that I am missing some basic steps, but the code above is what I have right now (I have about 500 lines of commented out code that doesn't work).
I've spent 2 weeks trying to learn how to do this but I need some help from someone who knows what I'm trying to do.
Any relevant links, videos or documentation would be awesome.. I am really stuck deep.
Thank you
EDIT: I have found a solution, and to all future readers who got this page from their 1000th Google search, what I did was change a bit in the models and add this HttpPost Create in my Controller.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(RentalCreationView rcw)
{
List<ParkingSpot> parkingSpots = _context.ParkingSpots.ToList();
List<Tenant> tenants = _context.Tenants.ToList();
RentalOwner ro = new RentalOwner
{
Tenant = tenants.Find(t => t.TenantId == rcw.TenantId),
OwnerSince = rcw.StartDate
};
Rental ren = new Rental
{
StartDate = rcw.StartDate,
ParkingSpot = parkingSpots.Find(ps => ps.ParkingSpotId == rcw.ParkingSpotId),
RentalOwner = ro
};
if (ModelState.IsValid)
{
_context.RentalOwners.Add(ro);
_context.Rentals.Add(ren);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
else
{
var errors = ModelState.Select(x => x.Value.Errors)
.Where(y => y.Count > 0)
.ToList();
}
return View();
}

Foreign Class not populating after save in mvc 4

I am saving data using Ajax and at the same time I want it to display in list. below is Class of City
public class City
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int StateId { get; set; }
[ForeignKey("StateId")]
public virtual State state { get; set; }
}
my Post method is
[HttpPost]
public ActionResult EditCity(City obj)
{
try
{
aRepository.Save(obj);
var db = aRepository.Citys.FirstOrDefault(c => c.Id == obj.Id);
return PartialView("_iCity", db);
}
catch (Exception e)
{
return Json("", JsonRequestBehavior.AllowGet);
}
}
Here db.state = null, it's not populating.
Should get you the state detail by :
or
var db = aRepository.Citys.FirstOrDefault(c => c.Id == obj.Id).Include(x=>x.StateId);
When you post the data for your server, your ViewModel should has StateId, if you are passing the Object state into it, by default Etity Framework will not persist.
I suppose you are using Lazy Loading , so you dont need to user Include to get the State entity

How can I edit an entity in MVC4 with EF5 which has a unique constraint?

[HttpPost]
public ActionResult Edit(Car car)
{
if (ModelState.IsValid)
{
db.Entry(car).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(car);
}
This is a controller method scaffolded by MCV 4
My "car" entity has a unique field: LicensePlate.
I have custom validation on my Entity:
Validation:
public partial class Car
{
partial void ValidateObject(ref List<ValidationResult> validationResults)
{
using (var db = new GarageIncEntities())
{
if (db.Cars.Any(c => c.LicensePlate.Equals(this.LicensePlate)))
{
validationResults.Add(
new ValidationResult("This licenseplate already exists.", new string[]{"LicensePlate"}));
}
}
}
}
should it be usefull, my car entity:
public partial class Car:IValidatableObject
{
public int Id { get; set; }
public string Color { get; set; }
public int Weight { get; set; }
public decimal Price { get; set; }
public string LicensePlate { get; set; }
public System.DateTime DateOfSale { get; set; }
public int Type_Id { get; set; }
public int Fuel_Id { get; set; }
public virtual CarType Type { get; set; }
public virtual Fuel Fuel { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var result = new List<ValidationResult>();
ValidateObject(ref result);
return result;
}
partial void ValidateObject(ref List<ValidationResult> validationResults);
}
QUESTION:
Everytime I edit a car, it raises an error:
Validation failed for one or more entities. See
'EntityValidationErrors' property for more details.
The error is the one raised by my validation, saying it can't edit because there is already a car with that license plate.
If anyone could point me in the right direction to fix this, that would be great!
I searched but couldn't find anything, so even related posts are welcome!
note: this field has a unique constraint, so it is imperative that this validation is still triggered for a create-action
Allright I found a fix but I'm not sure it's the best ever.
I modified the validation so that it only triggers when the Id is non existend (so.. 0).
This way, I can make a distiction between new entities and updated ones.
if (db.Cars.Any(c => c.LicensePlate.Equals(this.LicensePlate) && c.Id != this.Id))
This does fix my problem, but somehow I think there should be a cleaner fix.

Entity FrameWork 4.1 Exception

This is my code:
public void DeleteFolder(Entities.DocumentFolder folder)
{
DeleteFilesFromServer(folder.Id);
_dbContext.Entry(folder).State = EntityState.Deleted;
_dbContext.SaveChanges();
}
public void DeleteFilesFromServer(int id)
{
var allDocuments = _dbContext.Document.Where(x => x.FolderId == id).ToList();
foreach (var filePath in allDocuments.Select(document => HttpContext.Current.Server.MapPath("~/Documents/") + document.DocumentFileName).Where(System.IO.File.Exists))
{
System.IO.File.Delete(filePath);
}
}
public class DocumentFolder
{
public DocumentFolder()
{
Documents=new List<Document>();
}
public int Id { get; set; }
public string FolderName { get; set; }
public int ParentFolderId { get; set; }
public List<Document> Documents { get; set; }
}
public class Document
{
public int Id { get; set; }
public string DocumentName { get; set; }
public string DocumentFileName { get; set; }
public int FolderId { get; set; }
public virtual DocumentFolder Folder { get; set; }
}
By executing the delete operation I got the following exception:
System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
If I remove the DeleteFilesFromServer(int id) method the deletion working. Can someone help me?
If you want to delete the DocumentFolder, you need to delete the Document objects related to the DocumentFolder because in your model the field Folder is not nullable. This happens only if the dbContext knows that the the Document objects exist, i.e. if you load the documents with the Select method.

Entity Framework Id Value assign:

I have an entity defined as in a db First Model:
public class Merlin_BR_Condiciones_Item
{
public int IntIdGrupoCondiciones { get; set; }
public string StrCondicion { get; set; }
[Key]
public int IntIdCondicion { get; set; }
public virtual Merlin_BR_Condiciones_Item_Grupos Merlin_BR_Condiciones_Item_Grupos { get; set; }
}
And A controller generated automatically that has this create action:
public ActionResult Create(int pIntIdGrupoCondiciones = 0 )
{
ViewBag.IntIdGrupoCondiciones = new SelectList(db.Merlin_BR_Condiciones_Item_Grupos, "IntIdGrupoCondiciones", "StrDescripcionGrupo");
return View();
}
[HttpPost]
public ActionResult Create(Merlin_BR_Condiciones_Item merlin_br_condiciones_item)
{
if (ModelState.IsValid)
{
//================================================================================
// This section add the current key to IntIdCondicion
//================================================================================
var max = from c in db.Merlin_BR_Condiciones_Item
select c;
merlin_br_condiciones_item.IntIdCondicion = max.AsQueryable().Max(x => x.IntIdCondicion) + 1;
//================================================================================
db.Merlin_BR_Condiciones_Item.Add(merlin_br_condiciones_item);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.IntIdGrupoCondiciones = new SelectList(db.Merlin_BR_Condiciones_Item_Grupos, "IntIdGrupoCondiciones", "StrDescripcionGrupo", merlin_br_condiciones_item.IntIdGrupoCondiciones);
return View(merlin_br_condiciones_item);
}
This Entity has an Id Column assigned manually in the HttPost (create action).
The problem is that an error was idicating me that Can't insert NULL Value in IntIdCondicion column.
Following step by step the code the value allways return a valid key.
Tks for your help.
By default EF expects that all integer primary keys are generated in a database. So modify your mapping and tell EF that your primary key is not autogenerated:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int IntIdCondicion { get; set; }
If you are using EDMX you must configure StoreGeneratedPattern to None in IntIdCondicion properties.

Resources