I have some issue with relationships between 3 tables. There are many-to-many relationships between all of them - my model classes are shown below:
public partial class Bus
{
public Bus()
{
this.Lines = new HashSet<Line>();
this.Drivers = new HashSet<Driver>();
}
public int BusID { get; set; }
public string RegNum { get; set; }
[StringLength(3)]
public string Status { get; set; }
public virtual ICollection<Line> Lines { get; set; }
public virtual ICollection<Driver> Drivers { get; set; }
}
public partial class Driver
{
public Driver()
{
this.Buses = new HashSet<Bus>();
}
public int DriverID { get; set; }
public string DriverName { get; set; }
public string DriverSurname { get; set; }
[StringLength(3)]
public string Status { get; set; }
[Display(Name = "Driver")]
public string DriverInfo
{
get
{
return DriverName + " " + DriverSurname;
}
}
public virtual ICollection<Bus> Buses { get; set; }
}
public partial class Line
{
public Line()
{
this.Schedules = new HashSet<Schedule>();
this.Buses = new HashSet<Bus>();
}
public int LineID { get; set; }
public int LineNumber { get; set; }
public string Direction { get; set; }
[Display(Name = "Line: Direction")]
public string LineInfo
{
get
{
return LineNumber + ": " + Direction;
}
}
public virtual ICollection<Bus> Buses { get; set; }
}
DbContext:
public partial class ModelEntities : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Bus> Buses { get; set; }
public virtual DbSet<Driver> Drivers { get; set; }
public virtual DbSet<Line> Lines { get; set; }
}
According to: https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-a-more-complex-data-model-for-an-asp-net-mvc-application .
I handled with Bus<->Driver connection by creating ViewModels and updating BusController. I'm able to create and edit Bus using a checkbox (list of drivers) properly.
However, I have a problem to do the same with Bus<->Lines.
ViewModel folder consists of 3 classes (AssignedDriverData, BusIndexData, AssignedLineData):
public class AssignedDriverData
{
public int DriverID { get; set; }
public string DriverName { get; set; }
public string DriverSurname { get; set; }
public string DriverInfo
{
get
{
return DriverName + " " + DriverSurname;
}
}
public bool Assigned { get; set; }
}
public class BusIndexData
{
public IEnumerable<Bus> Buses { get; set; }
public IEnumerable<Driver> Drivers { get; set; }
public IEnumerable<Line> Lines { get; set; }
}
public class AssignedLineData
{
public int LineID { get; set; }
public int LineNumber { get; set; }
public string Direction { get; set; }
public string LineInfo
{
get
{
return LineNumber + ": " + Direction;
}
}
public bool Assigned { get; set; }
}
BusController (included changes line creating and editing):
public class BusesController : Controller
{
private ModelEntities db = new ModelEntities();
// GET: Buses
public ActionResult Index()
{
return View(db.Buses.ToList());
}
// GET: Buses/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Bus bus = db.Buses.Find(id);
if (bus == null)
{
return HttpNotFound();
}
return View(bus);
}
// GET: Buses/Create
public ActionResult Create()
{
//***************** adding drivers ******************//
var bus = new Bus();
bus.Drivers = new List<Driver>();
PopulateAssignedDriverData(bus);
bus.Lines = new List<Line>(); //********* adding lines*********************//
PopulateAssignedLineData(bus); //********* adding lines*********************//
//************************************************//
return View();
}
// POST: Buses/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "BusID,RegNum,Status")] Bus bus, string[] selectedDrivers, string[] selectedLines)
{
//******************* adding drivers **********************//
if (selectedDrivers != null)
{
bus.Drivers = new List<Driver>();
foreach (var course in selectedDrivers)
{
var driverToAdd = db.Drivers.Find(int.Parse(course));
bus.Drivers.Add(driverToAdd);
}
}
//************************************************//
//******************* adding lines **********************//
if (selectedLines != null)
{
bus.Lines = new List<Line>();
foreach (var line in selectedLines)
{
var lineToAdd = db.Lines.Find(int.Parse(line));
bus.Lines.Add(lineToAdd);
}
}
//************************************************//
if (ModelState.IsValid)
{
db.Buses.Add(bus);
db.SaveChanges();
return RedirectToAction("Index");
}
//************************************************//
PopulateAssignedDriverData(bus);
PopulateAssignedLineData(bus);
//************************************************//
return View(bus);
}
// GET: Buses/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//************** editing drivers ********************//
Bus bus = db.Buses
.Include(i => i.Drivers)
.Include(i => i.Lines) //****** for editing lines ******//
.Where(i => i.BusID == id)
.Single();
PopulateAssignedDriverData(bus);
//************************************************//
if (bus == null)
{
return HttpNotFound();
}
return View(bus);
}
// POST: Buses/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
//************** editing with drivers and lines ********************//
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int? id, string[] selectedDrivers, string[] selectedLines)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var busToUpdate = db.Buses
.Include(i => i.Drivers)
.Include(i => i.Lines) //****** added for lines *******//
.Where(i => i.BusID == id)
.Single();
if (TryUpdateModel(busToUpdate, "",
new string[] { "BusID,RegNum,Status" }))
{
try
{
UpdateBusDrivers(selectedDrivers, busToUpdate);
UpdateBusDrivers(selectedLines, busToUpdate); //****** added for lines *******//
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
PopulateAssignedDriverData(busToUpdate);
PopulateAssignedLineData(busToUpdate); //****** added for lines *******//
return View(busToUpdate);
}
//************************************************//
// GET: Buses/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Bus bus = db.Buses.Find(id);
if (bus == null)
{
return HttpNotFound();
}
return View(bus);
}
// POST: Buses/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Bus bus = db.Buses.Find(id);
db.Buses.Remove(bus);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
//********************** adding drivers ******************//
private void PopulateAssignedDriverData(Bus bus)
{
var allDrivers = db.Drivers;
var busDrivers = new HashSet<int>(bus.Drivers.Select(c => c.DriverID));
var viewModel = new List<AssignedDriverData>();
foreach (var driver in allDrivers)
{
viewModel.Add(new AssignedDriverData
{
DriverID = driver.DriverID,
DriverName = driver.DriverName,
DriverSurname = driver.DriverSurname,
Assigned = busDrivers.Contains(driver.DriverID)
});
}
ViewBag.Drivers = viewModel;
}
//************************************************//
//**************** editing drivers ***********************//
private void UpdateBusDrivers(string[] selectedDrivers, Bus busToUpdate)
{
if (selectedDrivers == null)
{
busToUpdate.Drivers = new List<Driver>();
return;
}
var selectedDriversHS = new HashSet<string>(selectedDrivers);
var busDrivers = new HashSet<int>
(busToUpdate.Drivers.Select(c => c.DriverID));
foreach (var driver in db.Drivers)
{
if (selectedDriversHS.Contains(driver.DriverID.ToString()))
{
if (!busDrivers.Contains(driver.DriverID))
{
busToUpdate.Drivers.Add(driver);
}
}
else
{
if (busDrivers.Contains(driver.DriverID))
{
busToUpdate.Drivers.Remove(driver);
}
}
}
}
//************************************************//
//********************** adding lines ******************//
private void PopulateAssignedLineData(Bus bus)
{
var allLines = db.Lines;
var busLines = new HashSet<int>(bus.Lines.Select(c => c.LineID));
var viewModel = new List<AssignedLineData>();
foreach (var line in allLines)
{
viewModel.Add(new AssignedLineData
{
LineID = line.LineID,
Direction = line.Direction,
LineNumber = line.LineNumber,
Assigned = busLines.Contains(line.LineID)
});
}
ViewBag.Lines = viewModel;
}
//************************************************//
//**************** editing lines ***********************//
private void UpdateBusLines(string[] selectedLines, Bus busToUpdate)
{
if (selectedLines == null)
{
busToUpdate.Lines = new List<Line>();
return;
}
var selectedLinesHS = new HashSet<string>(selectedLines);
var busLines = new HashSet<int>
(busToUpdate.Lines.Select(c => c.LineID));
foreach (var line in db.Lines)
{
if (selectedLinesHS.Contains(line.LineID.ToString()))
{
if (!busLines.Contains(line.LineID))
{
busToUpdate.Lines.Add(line);
}
}
else
{
if (busLines.Contains(line.LineID))
{
busToUpdate.Lines.Remove(line);
}
}
}
}
//************************************************//
}
Unfortunately, adding any lines to the bus failed.
How to handle with this 2 many-to-many relationship for Bus table?
I'd appreciate your hints ;)
KB
Have you read about Fluent API?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bus>().HasMany(b => b.Drivers).WithMany(d => d.Buses).Map(m =>
{
m.MapLeftKey("BusId");
m.MapRightKey("DriverID");
m.ToTable("BusDriverJoinTable");
});
modelBuilder.Entity<Bus>().HasMany(b => b.Lines).WithMany(l=>l.Buses).Map(m =>
{
m.MapLeftKey("BusId");
m.MapRightKey("LineID");
m.ToTable("BusLineJoinTable");
});
}
Related
When a new InvoiceItem is created, its InvoiceItemId is generated. For some reason, whenever I call OnPost page handler in EditInvoiceModel, the ids of my items in InputModel get generated again.
InvoiceItem
public class InvoiceItem
{
public string InvoiceItemId { get; set; }
public InvoiceItem()
{
InvoiceItemId = GenerateID.GenerateItemID();
}
}
}
EditInvoiceModel
public class EditInvoiceModel : PageModel
{
public readonly InvoiceService _service;
[BindProperty]
public InputModel Input { get; set; }
public string InvoiceId { get; set; }
public EditInvoiceModel(InvoiceService service)
{
_service = service;
}
public async void OnGet(string id)
{
Invoice invoice = await _service.GetInvoice(id);
Input = new InputModel();
Input.Items = invoice.Items;
}
public async Task<IActionResult> OnPost(string id)
{
if(ModelState.IsValid)
{
_service.EditInvoice(Input, id);
return RedirectToPage("/ViewInvoice", new { id = id });
}
return Page();
}
}
CreateInvoiceModel
public class CreateInvoiceModel : PageModel
{
public readonly InvoiceService _service;
[BindProperty]
public InputModel Input { get; set; }
public CreateInvoiceModel(InvoiceService service)
{
_service = service;
}
public void OnGet()
{
Input = new InputModel();
Input.PopulateItems();
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
_service.AddInvoice(Input);
return RedirectToPage("/Index");
}
return Page();
}
}
My Input Model
public class InputModel
{
public List<InvoiceItem> Items { get; set; } = new List<InvoiceItem>(16);
public void PopulateItems()
{
for (int i = 0; i < Items.Capacity; i++)
{
Items.Add(new InvoiceItem());
}
}
}
EditInvoice Service
public async void EditInvoice(InputModel input, string id)
{
var invoice = await _context.Invoices.Include(x => x.Client).Include(x => x.Items).FirstAsync(x => x.InvoiceId == id);
if (invoice == null) { throw new Exception("Unable to find the invoice"); }
invoice.Items = input.Items;
invoice.Description = input.Description;
invoice.InvoiceDate = input.InvoiceDate;
invoice.PaymentTerms = input.PaymentTerms;
invoice.Client = input.Client;
invoice.BillFromAddress = input.BillFromAddress;
invoice.BillFromCity = input.BillFromCity;
invoice.BillFromCountry = input.BillFromCountry;
invoice.BillFromPostal = input.BillFromPostal;
_context.Entry(invoice).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
I'm getting a Bad Request when I'm sending a POST request to the Klarna API to create a new order.
This is my code for sending the POST request:
Cart = new CartManager(_context, HttpContext.Session).GetCart();
Customer = new CustomerManager(HttpContext.Session).GetCustomer()
OrderViewModel order = new OrderViewModel();
order.Reference = DateTime.Now.ToOADate().ToString().Replace(",", string.Empty);
order.Cart = Cart;
order.Customer = Customer;
string url = ApiHelper.KlarnaApiClient.BaseAddress + "checkout/v3/orders";
KlarnaOrderModel klarnaOrderModel = new KlarnaOrderModel
{
purchase_currency = "SEK",
order_amount = (int)order.Cart.TotalCharge,
order_lines = klarnaOrderLines
};
HttpResponseMessage response = await ApiHelper.KlarnaApiClient.PostAsJsonAsync(
url, klarnaOrderModel);
response.EnsureSuccessStatusCode();
KlarnaOrderModel:
public class KlarnaOrderModel
{
public string purchase_country { get { return "SE"; } }
public string purchase_currency { get; set; }
public string locale { get { return "en-GB"; } }
public int order_amount { get; set; }
public int order_tax_amount { get { return 2500; } }
public List<KlarnaOrderLine> order_lines { get; set; }
public KlarnaMerchantUrls merchant_urls { get { return new Models.KlarnaMerchantUrls(); } }
}
KlarnaOrderLine:
public class KlarnaOrderLine
{
public string name { get; set; }
public int quantity { get; set; }
public int unit_price { get; set; }
public int tax_rate { get { return 2500; } }
public int total_amount { get { return unit_price * quantity; } }
public int total_tax_amount { get { return total_amount / 5 ; } }
}
KlarnaMerchantUrls:
public class KlarnaMerchantUrls
{
public string terms { get { return "https://localhost:44316/shop/terms"; } }
public string checkout { get { return "https://localhost:44316/shop/checkout"; } }
public string confirmation { get { return "https://localhost:44316/shop/checkout/confirmation"; }
public string push { get { return "https://localhost:44316/shop/push"; } }
}
Here is a screenshot:
My code for initializing the API:
KlarnaApiClient = new HttpClient();
KlarnaApiClient.BaseAddress = new Uri("https://api.playground.klarna.com/");
KlarnaApiClient.DefaultRequestHeaders.Accept.Clear();
KlarnaApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
KlarnaApiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"{MY KLARNA API KEY UID}:{MY KLARNA API KEY PASSWORD}")));
i need to add some data in OptionRoleTable:
public class OptionRole
{
public int Id { get; set; }
public int RoleId { get; set; }
public int OptionsId { get; set; }
public virtual Role Role { get; set; }
public virtual Options Options { get; set; }
}
and this is Options Tabel:
public partial class Options
{
public int Id { get; set; }
public string OptionName { get; set; }
public string RouteFunctionName { get; set; }
public string Icon { get; set; }
public virtual ICollection<OptionRole> OptionRoles { get; set; }
}
i must check data not exist in OptionRole , when i using this code for add data in OptionRole :
public async Task<Options> findOptionsId(int optionId)
{
return await _option.FirstOrDefaultAsync(x => x.Id == optionId);
}
public async Task<bool> AddorUpdateOptions(int optionId, IList<int> selectedRoleValue)
{
List<OptionVM> optionVMs = new List<OptionVM>();
List<int> currentOptionValue = new List<int>();
var optionRole = await findOptionsId(optionId);
if (optionRole == null)
{
return false;
}
foreach (var item in selectedRoleValue)
{
var findRole = await _roleManager.FindByIdAsync(item);
var findOPR = optionRole.OptionRoles.FirstOrDefault(x => x.OptionsId== optionId && x.RoleId==item);
if (findOPR != null)
{
currentOptionValue.Add(item);
}
}
if (selectedRoleValue == null)
{
selectedRoleValue = new List<int>();
}
var newOptionRole = selectedRoleValue.Except(currentOptionValue).ToList();
foreach (var opRole in newOptionRole)
{
var findRole = await _roleManager.FindByIdAsync(opRole);
if (findRole != null)
{
optionRole.OptionRoles.Add(new OptionRole
{
OptionsId = optionRole.Id,
RoleId = findRole.Id
});
}
}
var removeOptionRole = currentOptionValue.Except(selectedRoleValue).ToList();
foreach (var remove in removeOptionRole)
{
var findOptionRole = _optionRoles.FirstOrDefault(x => x.Id == remove);
if (findOptionRole != null)
{
optionRole.OptionRoles.Remove(findOptionRole);
}
}
return Update(optionRole.OptionRoles);
}
I must have pass a class type of Options when i using this code . it show me this Error :
Severity Code Description Project File Line Suppression State
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.ICollection' to 'StoreFinal.Entities.Entities.Identity.OptionRole' StoreFinal.Services C:\Users\Mr-Programer\Desktop\New folder\StoreFinal\StoreFinal.Services\Contracts\Identity\Service\ApplicationOptionRoleManager.cs 97 Active
Error in this line : return Update(optionRole.OptionRoles);
whats the problem ? how can i solve this problem ?
Edit :
Update Method :
public virtual bool Update(T entity)
{
try
{
Entities.Attach(entity);
return true;
}
catch (Exception)
{
return false;
}
}
Look at the Update Method signature:
public virtual bool Update(T entity);
It accepts a param type T which should be One Entity - Why One Entity -- because Entities.Attach() accepts only 1 Object. While what you are passing to it is:
return Update(optionRole.OptionRoles);
Where OptionRoles is of type: ICollection<OptionRole> --
For understandings sake, Change it to
return Update(optionRole.OptionRoles[0]);
or
return Update(optionRole.OptionRoles.First());
And then share the result.
When user update the property Curso (type Curso), this update does not work in Entity Framework. All data in object of type Turma are updated, but property Curso. Follow bellow my code:
This is the data received by PUT method in MVC controller:
{
"Id":1,
"DataVencimento":"2017-11-24T00:00:00",
"Nome":".Net MVCss",
"Turno":"Tarde",
"Curso":
{
"Id":1,
"Nome":"teste",
"Duracao":2,
"ValorAnuidade":5888.88,
"QtdParcelas":2,
"ValorParcela":22.22,
"ValorMatricula":22.22,
"Disciplinas":null,
"CorpoDocente":null,
"Documentos":null,
"Turmas":null
}
}
This is PUT method in MVC Controller:
[HttpPut]
public HttpResponseMessage Update(TurmaDto dto)
{
var response = new HttpResponseMessage();
IKernel ninjectKernel = new StandardKernel();
ninjectKernel.Bind<ITurmaBLO>().To<TurmaBLO>();
ITurmaBLO blo = ninjectKernel.Get<ITurmaBLO>();
Turma t = Mapper.Map<TurmaDto, Turma>(dto);
if (!blo.Update(t))
{
response = Request.CreateResponse(HttpStatusCode.NotFound, "Turma não encontrada.");
}
else
{
response = Request.CreateResponse(HttpStatusCode.OK, t);
}
return response;
}
This is Class TurmaBLO:
public class TurmaBLO : GenericaBLO<Turma>, ITurmaBLO
{
private IKernel ninjectKernel = new StandardKernel();
private ITurmaDAO _dao;
public TurmaBLO()
{
ninjectKernel.Bind<ITurmaDAO>().To<TurmaDAO>();
_dao = ninjectKernel.Get<ITurmaDAO>();
}
public override bool Add(Turma e)
{
return _dao.Add(e);
}
public override bool Update(Turma e)
{
return _dao.Update(e);
}
public override List<Turma> GetAll()
{
return _dao.GetAll();
}
public override Turma Get(int id)
{
return _dao.Get(id);
}
}
This is Class GenericaBLO:
public class GenericaBLO<T> : IGenericaBLO<T> where T : class
{
public GenericaDAO<T> dao;
public virtual bool Add(T e)
{
dao = new GenericaDAO<T>();
return dao.Add(e);
}
public virtual bool Update(T e)
{
dao = new GenericaDAO<T>();
return dao.Update(e);
}
public virtual bool Delete(T e)
{
dao = new GenericaDAO<T>();
return dao.Delete(e);
}
public virtual List<T> GetAll()
{
dao = new GenericaDAO<T>();
return dao.GetAll();
}
public virtual T Get(int id)
{
dao = new GenericaDAO<T>();
return dao.Get(id);
}
public void ValidateForAdd()
{
throw new NotImplementedException();
}
public void ValidateForUpdate()
{
throw new NotImplementedException();
}
public void ValidateForDelete()
{
throw new NotImplementedException();
}
}
This is Class TurmaDAO:
internal class TurmaDAO : GenericaDAO<Turma>, ITurmaDAO
{
public override bool Add(Turma e)
{
base.Context.Curso.Attach(e.Curso);
return base.Add(e);
}
public override bool Update(Turma e)
{
base.Context.Curso.Attach(e.Curso);
return base.Update(e);
}
public override List<Turma> GetAll()
{
return base.Context.Turma.Include(c => c.Curso).Include(dt => dt.Descontos).ToList();
}
public override Turma Get(int id)
{
return base.Context.Turma.Include(c => c.Curso).Include(dt => dt.Descontos).SingleOrDefault(c => c.Id == id);
}
}
This is Class GenericaDAO:
public class GenericaDAO<T> : IGenericaDAO<T> where T : class
{
internal ApplicationDbContext Context { get; set; }
protected DbSet<T> DbSet { get; set; }
public GenericaDAO()
{
Context = new ApplicationDbContext();
DbSet = Context.Set<T>();
}
public virtual bool Add(T e)
{
try
{
Context.Entry(e).State = EntityState.Added;
Context.SaveChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public virtual bool Update(T e)
{
try
{
Context.Entry(e).State = EntityState.Modified;
Context.SaveChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public virtual bool Delete(T e)
{
try
{
Context.Entry(e).State = EntityState.Deleted;
Context.SaveChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public virtual List<T> GetAll()
{
return DbSet.ToList();
}
public virtual T Get(int id)
{
return DbSet.Find(id);
}
}
This is Class TurmaDto:
public class TurmaDto
{
public int Id { get; set; }
public DateTime DataVencimento { get; set; }
public string Nome { get; set; }
public string Turno { get; set; }
public CursoDto Curso { get; set; }
}
This is Class Turma:
public class Turma
{
public int Id { get; set; }
public DateTime DataVencimento { get; set; }
public string Nome { get; set; }
public string Turno { get; set; }
public Curso Curso { get; set; }
}
You also need to call Update on the child entities, in your case Curso. So:
ITurmaBLO bloT = ninjectKernel.Get<ITurmaBLO>();
ICursoBLO blo = ninjectKernel.Get<ICursoBLO>();
Turma t = Mapper.Map<TurmaDto, Turma>(dto);
if(!(bloT.Update(t) && bloC.Update(t.Curso))){
response = Request.CreateResponse(HttpStatusCode.NotFound, "Turma não encontrada.");
}
If you need to only update the relation from Turma to Curso without changing the Curso, it's enough to set Turma.CursoId to the Id of the new Curso, assuming the relationship is mapped correctly in EF.
I'm currently working on a small application with WPF, EF6 and SqlServer 2012. I have two entities "Region" and "BctGouvernorats" associated with an optional one to many relationship.
My problem is : When I remove a child (BctGouvernorat) from the relationship , it still appears in the collection related to the parent (Region). here's the code:
//Entities
public partial class BctGouvernorat
{
public long GovId { get; set; }
public string Libelle { get; set; }
public long UserId { get; set; }
public Nullable<long> RegionId { get; set; }
public virtual Region Region { get; set; }
}
public partial class Region
{
public long RegionId { get; set; }
public string Libelle { get; set; }
public long GroupeNumber { get; set; }
public byte Bonus { get; set; }
public long UserId { get; set; }
public virtual RegionsGroupes GroupeRegions { get; set; }
public virtual ICollection<BctGouvernorat> Gouvernorats { get; set; }
public Region()
{
Libelle = "New region";
GroupeNumber = 0;
this. Gouvernorats = new HashSet<BctGouvernorat>() ;
}
//Mapping of BctGouvernorat entity
public BctGouvernoratMapping()
{
this.ToTable("BctGouvernorat");
this.HasKey(t => t.GovId);
this.Property(t => t.GovId);
this.HasOptional(t => t.Region)
.WithMany(t => t.Gouvernorats)
.HasForeignKey(d => d.RegionId)
.WillCascadeOnDelete(false);
}
//Mapping of Region entity
public RegionMapping()
{
this.ToTable("Region");
this.HasKey(t => t.RegionId);
this.Property(t => t.RegionId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
//C# code for "Modify" Method
public void Modify(Region r, List<BctGouvernorat> _ToUnlink, List<BctGouvernorat> _ToLink)
{
//Below the code for unlink child from parent
if (_ToUnlink.Count > 0)
{
r.Gouvernorats.ToList().All(xx =>
{
if (_ToUnlink.Contains(xx))
{
xx.RegionId = null;
xx.Region = null;
}
return true;
}
);
}
//Here the code for link child to the parent
_ToLink.All(xx =>
{
xx.RegionId = r.RegionId;
xx.Region = r;
r.Gouvernorats.Add(xx);
return true;
});
//Mark Childs collection as modified
r.Gouvernorats.All(xx =>
{
_uow.GetEntry<BctGouvernorat>(xx).State = EntityState.Modified;
return true;
});
base.Modify(r);
}
actually the previous method is included in a «RegionRepository» which inherits from a base class Repository. The code of base.Modify() is as follows :
//Method Modify from RegionRepository
public void Modify(T item)
{
_uow.RegisterChanged(item);
_uow.Commit();
}
And Modify Method uses services of a unit of work "_uow" that save data to sqlserver database. Here the code :
//***************************
//_uow is a unit of work
//*****************************
public void RegisterChanged<T>(T item) where T : class
{
base.Entry<T>(item).State = System.Data.Entity.EntityState.Modified;
}
public void Commit()
{
try
{
base.SaveChanges();
}
catch (DbUpdateException e)
{
var innerEx = e.InnerException;
while (innerEx.InnerException != null)
innerEx = innerEx.InnerException;
throw new Exception(innerEx.Message);
}
catch (DbEntityValidationException e)
{
var sb = new StringBuilder();
foreach (var entry in e.EntityValidationErrors)
{
foreach (var error in entry.ValidationErrors)
{
sb.AppendLine(string.Format("{0}-{1}-{2}",
entry.Entry.Entity,
error.PropertyName,
error.ErrorMessage
));
}
}
throw new Exception(sb.ToString());
}
}
Sorry, I should have put the ViewModel code that calls the previous code :
private void SaveRegion()
{
List<BctGouvernorat> _GovToLink = null;
//The following method checks and returns (Added, Deleted, Modified BctGouvernorat)
List<BctGouvernorat> _GovToUnlink = CheckGouvernoratsListStatus(out _GovToLink);
ILogger _currentLog = (Application.Current as App).GetCurrentLogger();
using (UnitOfWork cx = new UnitOfWork(_currentLog))
{
RegionRepository _regionRepository = new RegionRepository(cx, _currentLog);
IRegionManagementService rms = new RegionManagementService(_currentLog, _regionRepository);
if (CurrentRegion.RegionId == 0)
{
CurrentRegion.UserId = Session.GetConnectedUser().UserId;
rms.AddRegion(CurrentRegion);
}
else
rms.ModifyRegion(CurrentRegion, _GovToUnlink,_GovToLink);
}
}
private List<BctGouvernorat> CheckGouvernoratsListStatus(out List<BctGouvernorat> _ToLink)
{
List<BctGouvernorat> AddedGouvernorats = GouvernoratsRegion.Except<BctGouvernorat>(CurrentRegion.Gouvernorats,
new GouvernoratComparer()).ToList();
_ToLink = AddedGouvernorats;
List<BctGouvernorat> DeletedGouvernorats = CurrentRegion.Gouvernorats.Except<BctGouvernorat>(GouvernoratsRegion,
new GouvernoratComparer()).ToList();
return DeletedGouvernorats;
}
The "GouvernoratsRegion" is an observablecollection bound to a datagrid that i edit to add or remove BCTgouvernorat Rows to the region
public void ModifyRegion(Region r, List<BctGouvernorat> _ToUnlik, List<BctGouvernorat> _ToLink)
{
_regionRepository.Modify(r, _ToUnlik, _ToLink);
}