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.
Related
I have tried to Eagerly load all the data related to an entity, but I still have a problem regarding the recusive properties like this one :
public class Node : BaseAbstractEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[ForeignKey("TypeId")]
public virtual NodeType Type { get; set; }
public int? TypeId { get; set; }
[ForeignKey("ParentId")]
public virtual Node Parent { get; set; }
public int? ParentId { get; set; }
public ICollection<Node> Children { get; set; }
}
I am using it in this method :
public async Task<object> JustGetAsync(Type type, JObject value, DbContext context)
{
int id = 0;
if (value != null && value["id"] != null)
id = Int32.Parse(value["id"].ToString());
if (id != 0)
return await context.FindAsync(type, id);
var TypeSet = (IQueryable<object>) context.GetType()
.GetMethod("Set")
.MakeGenericMethod(type)
.Invoke(context, null);
return await TypeSet.Include(context.GetIncludePaths(type)).ToListAsync();
}
the get IncludePaths is a code that I found here enter link description here that helps to eager all the properties :
public static IQueryable Include(this IQueryable source, IEnumerable navigationPropertyPaths)
where T : class
{
return navigationPropertyPaths.Aggregate(source, (query, path) => query.Include(path));
}
public static IEnumerable<string> GetIncludePaths(this DbContext context, Type clrEntityType)
{
var entityType = context.Model.FindEntityType(clrEntityType);
var includedNavigations = new HashSet<INavigation>();
var stack = new Stack<IEnumerator<INavigation>>();
while (true)
{
var entityNavigations = new List<INavigation>();
foreach (var navigation in entityType.GetNavigations())
{
if (includedNavigations.Add(navigation))
entityNavigations.Add(navigation);
}
if (entityNavigations.Count == 0)
{
if (stack.Count > 0)
yield return string.Join(".", stack.Reverse().Select(e => e.Current.Name));
}
else
{
foreach (var navigation in entityNavigations)
{
var inverseNavigation = navigation.FindInverse();
if (inverseNavigation != null)
includedNavigations.Add(inverseNavigation);
}
stack.Push(entityNavigations.GetEnumerator());
}
while (stack.Count > 0 && !stack.Peek().MoveNext())
stack.Pop();
if (stack.Count == 0) break;
entityType = stack.Peek().Current.GetTargetType();
}
}
I want to compare all of my list of generic T property value to my local variable searchText.
i already try:
x.GetType().GetProperties().All(props => props.GetValue(x).ToString().ToLower().Contains(searchText.ToLower()))
and i get error : NullReferenceException: Object reference not set to an instance of an object.
here my full method :
protected List<T> ProcessCollection<T>(List<T> lstElements, IFormCollection requestFormData, Func<T, IComparable> getProp)
{
string searchText = string.Empty;
Microsoft.Extensions.Primitives.StringValues tempOrder = new[] { "" };
if (requestFormData.TryGetValue("search[value]", out tempOrder))
{
searchText = requestFormData["search[value]"].ToString();
}
var skip = Convert.ToInt32(requestFormData["start"].ToString());
var pageSize = Convert.ToInt32(requestFormData["length"].ToString());
if (requestFormData.TryGetValue("order[0][column]", out tempOrder))
{
var columnIndex = requestFormData["order[0][column]"].ToString();
var sortDirection = requestFormData["order[0][dir]"].ToString();
tempOrder = new[] { "" };
if (requestFormData.TryGetValue($"columns[{columnIndex}][data]", out tempOrder))
{
var columName = requestFormData[$"columns[{columnIndex}][data]"].ToString();
if (pageSize > 0)
{
var prop = GetProperty<T>(columName);
if (sortDirection == "asc")
{
return lstElements.Where(x=>x.GetType().GetProperties().All(props => props.GetValue(x).ToString().ToLower().Contains(searchText.ToLower()))
.Skip(skip).Take(pageSize).OrderBy(b => prop.GetValue(b)).ToList();
}
else
return lstElements
.Where(x => getProp(x).ToString().ToLower().Contains(searchText.ToLower()))
.Skip(skip).Take(pageSize).OrderByDescending(b => prop.GetValue(b)).ToList();
}
else
return lstElements;
}
}
return null;
}
this is how i call my method :
var listItem = ProcessCollection<Jtabel>(temp,requestFormData,x=>x.Name);
the temp that i pass to method already filled
and this is the class type that i pass to this method
public class Jtabel : BaseModel
{
public string Creator { get; set; }
public string Name { get; set; }
public int SO { get; set; }
public int SOD { get; set; }
public int SAP { get; set; }
public int BA { get; set; }
public int Invoice { get; set; }
public int EPD { get; set; }
public double DP { get; set; }
}
i will try to explain more detail as much i can, so i want to get all of my property from Jtabel class to my ProcessCollection where inside the method all of Jtable property will test each element that's contain searchText.
This is a continuation of another post. I'm trying to create an interface that will let me walk through a collection of objects, and access the name of the properties of the object.
A Report object will have ReportSections. A ReportSection will have an ICollection of items which will change depending on usage.
Here's how I'm trying to define it now.
public interface IReport
{
string ReportName { get; set; }
ICollection<IReportSection> ReportSections { get; }
}
public interface IReportSection
{
string ReportSectionName { get; set; }
ICollection ReportItems { get; }
}
public abstract class ReportBase : IReport
{
virtual public string ReportType { get; set; }
virtual public string ReportName { get; set; }
virtual public ICollection<IReportSection> ReportSections { get; set; }
}
public abstract class ReportSectionBase : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection ReportItems { get; set; }
}
In my code, I would do this:
public class BookAffiliates : ReportSectionBase
{
public override string ReportSectionName { get { return "Book Affiliates"; } }
public override ICollection ReportItems { get; set; }
}
public class SomeClass
{
public ICollection<AuthorsViewModel> Authors { get; set; }
public ICollection<ProjectSubmissionViewModel> Submissions { get; set; }
public string ProcessAuthorsReport()
{
var report = new ContribAuthorsReport{ ReportType = "CSV" };
var authorAffil = new BookAffiliates {ReportItems = Authors };
report.ReportSections.Add(chapAffil);
var submissionAffil = new BookAffiliates {ReportItems = Submissions};
report.ReportSections.Add(submissionAffil );
return RenderReport(report)
}
}
In RenderReport I would like to walk through the collections and use the PropertyNames:
private string RenderReport(ReportBase currentReport)
{
var reportBody = new StringBuilder();
reportBody.Append(currentReport.ReportName + Environment.NewLine + Environment.NewLine);
foreach (var thisSection in currentReport.ReportSections)
{
reportBody.Append(thisSection.ReportSectionName + Environment.NewLine);
/// ---- Here! Here! I don't know what type, I want the
/// code to get the type based on ReportSectionBase<T>
var firstItem = thisSection.ReportItems.OfType<???Type???>().FirstOrDefault();
// I would actually like to go through each property of
// the ReportItem type and list it here.
foreach(var prop in firstItem.GetType().GetProperties())
{
reportBody.AppendLine(string.Format("{0}:{1}" prop.Name, prop.Value));
}
}
return reportBody.ToString();
}
I'm not sure how to best define this. I'm pretty sure I've done it before, but it's not coming to me.
You would use Reflection to do it.
foreach(var prop in thisItem.GetType().GetProperties())
{
reportBody.AppendLine(string.Format("{0}:{1}" prop.Name, prop.Value));
}
Took a while, a lot of questions, and figuring out what I really wanted to ask. I came up with this.
Here are my interfaces and base classes:
public class ReportBase
{
public ReportBase()
{
ReportSections = new List<IReportSection>();
}
public string ReportType { get; set; }
public string ReportName { get; set; }
public ICollection<IReportSection> ReportSections { get; set; }
}
public interface IReportSection
{
string ReportSectionName { get; }
ICollection ReportItems { get; set; }
}
public class ReportSection<T> : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection<T> ReportItems { get; set; }
ICollection IReportSection.ReportItems
{
get { return ReportItems as ICollection; }
set { ReportItems = value as ICollection<T>; }
}
}
I create my objects like this:
public ReportBase GetContribAuthorsReport
(
ICollection<ProjectAffiliateViewModel> projectAffiliates,
ICollection<SubmissionAffiliateViewModel> submissionAffiliates
)
{
//var caReport = new ContributingAffiliates("CSV", projectAffiliates, submissionAffiliates);
var caReport = new ReportBase { ReportType = "CSV", ReportName = "Reviewers' Contact Information" };
caReport.ReportSections.Add(new ReportSection<ProjectAffiliateViewModel> { ReportItems = projectAffiliates });
caReport.ReportSections.Add(new ReportSection<SubmissionAffiliateViewModel> { ReportItems = submissionAffiliates });
return caReport;//.Report;
}
Then I iterate through the objects like this:
public class DownloadCsvActionResult : ActionResult
{
public ReportBase Report { get; set; }
public string fileName { get; set; }
private string ReportData { get; set; }
public DownloadCsvActionResult(ReportBase report, string pFileName)
{
Report = report;
fileName = pFileName;
ReportData = RenderReport();
}
private string RenderReport()
{
var reportBody = new StringBuilder();
reportBody.AppendLine(Report.ReportName);
reportBody.Append(Environment.NewLine + Environment.NewLine);
foreach (var thisSection in Report.ReportSections)
{
reportBody.Append(thisSection.ReportSectionName + Environment.NewLine);
if (thisSection.ReportItems != null)
{
var itemType = thisSection.ReportItems.GetType().GetGenericArguments().Single();
var first = true;
foreach (var prop in itemType.GetProperties())
{
if (!first) reportBody.Append(",");
DisplayAttribute attribute = prop.GetCustomAttributes(typeof(DisplayAttribute), false)
.Cast<DisplayAttribute>()
.SingleOrDefault();
string displayName = (attribute != null) ? attribute.Name : prop.Name;
reportBody.Append(displayName);
first = false;
}
reportBody.Append(Environment.NewLine);
foreach (var thisItem in thisSection.ReportItems)
{
var firstData = true;
foreach (var prop in itemType.GetProperties())
{
if (!firstData) reportBody.Append(",");
reportBody.Append(prop.GetValue(thisItem,null));
firstData = false;
}
reportBody.Append(Environment.NewLine);
}
}
reportBody.Append(Environment.NewLine);
}
return reportBody.ToString();
}
public override void ExecuteResult(ControllerContext context)
{
//Create a response stream to create and write the Excel file
HttpContext curContext = HttpContext.Current;
curContext.Response.Clear();
curContext.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
curContext.Response.Charset = "";
curContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
curContext.Response.ContentType = "application/vnd.ms-excel";
//Write the stream back to the response
curContext.Response.Write(ReportData);
curContext.Response.End();
}
}
This gives me what I need for now. Sorry I wasn't as clear in the first place, and thank you for all your help.
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");
});
}
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);
}