Entity framwork 6 Code First and optional foreign key issue - c#

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);
}

Related

cannot convert from 'System.Collections.Generic.ICollection<x>' to 'x'

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.

ef core .Include().Contains() null reference exception

I have my models setup like this...
public class Model1 : IEquatable<Model1>
{
public int Model1Id { get; set; }
public string Name1 { get; set; }
public Model2 Model2 { get; set; }
public int Model2Id { get; set; }
public bool Equals(Model1 other)
{
return this.Model2.Equals(other.Model2)
&& this.Name1 == other.Name1;
}
}
public class Model2 : IEquatable<Model2>
{
public int Model2Id { get; set; }
public string Name2 { get; set; }
public bool Equals(Model2 other)
{
return this.Name2 == other.Name2;
}
}
public class ModelContext : DbContext
{
public DbSet<Model1> Model1 { get; set; }
public DbSet<Model2> Model2 { get; set; }
public ModelContext(DbContextOptions<ModelContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model1>(b =>
{
b.HasOne(m1 => m1.Model2).WithMany().HasForeignKey(m1 => m1.Model2Id);
});
}
}
then I get a null reference exception when I do this...
static void Main(string[] args)
{
var myModel1 = new Model1
{
Name1 = "myName1",
Model2 = new Model2
{
Name2 = "myName2"
}
};
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
try
{
var options = new DbContextOptionsBuilder<ModelContext>()
.UseSqlite(connection)
.Options;
//create database
using(var ctx = new ModelContext(options))
{
ctx.Database.EnsureCreated();
}
//add model objects
using (var ctx = new ModelContext(options))
{
ctx.Database.EnsureCreated();
ctx.Model1.Add(myModel1);
ctx.SaveChanges();
}
//check if exists
using(var ctx = new ModelContext(options))
{
//exception here
bool isExists = ctx.Model1.Include(m1 => m1.Model2).Contains(myModel1);
Console.WriteLine(isExists);
}
}
finally
{
connection.Close();
}
Console.ReadKey();
}
I'm expeting the Model2 instance of my m1 to be populated when I call the Include but it is still null.
but If I add AsEnumerable() to my query like..
ctx.Model1.Include(m1 => m1.Model2).AsEnumerable().Contains(model1);
then everything works fine.
EDIT:
my question is... why do I need to call AsEnumerable()? I was expecting it to work without calling AsEnumerable()..
The difference is one is an entityframe work call the other is linq to objects
Entity Framework Does not understand contains for a CLR Object
public void AddIfNotExists(Model1 model1)
{
//No Need for the include this is executed in sql, assuming the model 2
//property has already been included in your model1 this should work fine
if(false == _context.Model1.Any(x => x.Name1 == model1.Name1
&& x.Model2.Name2 == model1.Model2.Name2))
{
_context.Model1.Add(model1);
}
}
I made this based off of your logic, but chances are you really just want to check if model1.id is the the model1 set. But I have no Idea what your architecture is doing so this is what you probably want

Two many-to-many relationships for one table

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");
});
}

Generic Repository Pattern Soft Delete

I have create a Generic Repository (Using EF 6.1.1), which I am using in several projects and it works very well.
I have implemented a 'soft' delete feature, where we mark data as deleted, but do not actually remove it from the database.
I can then filter all my queries as all entities inherit from a base entity which has the IsDeleted property. This is all works nicely, but it obviously does not filter out any of the 'soft deleted' child entities.
I am unsure how to go about doing this in a generic way, as I dont want to have to over code a solution into every respoitory, that really defeats the reason for having a generic repo.
this is an example of my current Generic Repo
public sealed class MyRepository<T> : IRepository<T> where T : BaseEntity
{
public String CurrentUser { get; set; }
private readonly MyObjectContext context;
private readonly Configuration configuration = ConfigurationManager.GetConfiguration();
private IDbSet<T> entities;
private IDbSet<T> Entities
{
get { return entities ?? (entities = context.Set<T>()); }
}
public MyRepository(MyObjectContext context, String userName = null)
{
this.context = context;
var providerManager = new DataProviderManager(configuration);
var dataProvider = (IDataProvider)providerManager.LoadDataProvider();
dataProvider.InitDatabase();
CurrentUser = userName;
}
public void Dispose()
{
//do nothing at the moment
}
public T GetById(Guid id)
{
return Entities.FirstOrDefault(x => x.Id == id && !x.IsDeleted);
}
public IQueryable<T> GetAll()
{
return Entities.Where(x => !x.IsDeleted);
}
public IQueryable<T> Query(Expression<Func<T, bool>> filter)
{
return Entities.Where(filter).Where(x => !x.IsDeleted);
}
public void Delete(T entity)
{
if (configuration.HardDelete)
{
HardDelete(entity);
}
else
{
SoftDelete(entity);
}
}
private void HardDelete(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
Entities.Attach(entity);
Entities.Remove(entity);
}
catch (DbEntityValidationException ex)
{
var msg = string.Empty;
foreach (var validationErrors in ex.EntityValidationErrors)
foreach (var validationError in validationErrors.ValidationErrors)
msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
var fail = new Exception(msg, ex);
throw fail;
}
}
private void SoftDelete(T entity)
{
entity.IsDeleted = true;
Update(entity);
}
}
Any help on this would be great.
Thanks
Someone has built a global filter, you can try it and install it from nuget EntityFramework.Filters.
https://github.com/jbogard/EntityFramework.Filters
Here is an example how to use it.
public abstract class BaseEntity
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
public class Foo : BaseEntity
{
public string Name { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar : BaseEntity
{
public string Name { get; set; }
public int FooId { get; set; }
public Foo Foo { get; set; }
}
public class AppContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Registers and configures it first.
DbInterception.Add(new FilterInterceptor());
var softDeleteFilter = FilterConvention.Create<BaseEntity>("SoftDelete",
e => e.IsDeleted == false); // don't change it into e => !e.IsDeleted
modelBuilder.Conventions.Add(softDeleteFilter);
}
}
Then you can enable it in your repository constructor or somewhere after db context instance is created because the filters are disabled by default.
using (var db = new AppContext())
{
db.EnableFilter("SoftDelete");
var foos = db.Foos.Include(f => f.Bars).ToArray(); // works on Include
}
using (var db = new AppContext())
{
db.EnableFilter("SoftDelete");
var foos = db.Foos.ToArray();
foreach (var foo in foos)
{
var bars = foo.Bars; // works on lazy loading
}
}
using (var db = new AppContext())
{
db.EnableFilter("SoftDelete");
var foos = db.Foos.ToArray();
foreach (var foo in foos)
{
db.Entry(foo).Collection(f => f.Bars).Load(); // works on manual loading
}
}
This filter is not needed anymore.
public IQueryable<T> Query(Expression<Func<T, bool>> filter)
{
return Entities.Where(filter);//.Where(x => !x.IsDeleted);
}
As long as you have enabled it.
public MyRepository(MyObjectContext context, String userName = null)
{
this.context = context;
if (!configuration.HardDelete)
{
this.context.EnableFilter("SoftDelete");
}
}
I was having the same problem but my method to solve was little different i used a genric base interface IGenricInterface with IsDeleted as property
public int DeletebyId(string Id)
{
var Ent = (IGenricInterface)_sitecontext.Set<TEntity>().Find(Id);
Ent.IsDeleted = 1;
}
and for hardDelete
_sitecontext.Set<TEntity>().Remove(Ent);
This is on ID but offcourse you can do it on EnTity as well

Singleton with Entity Framework : Will Queries run multiple times?

With a model such as ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.ComponentModel.DataAnnotations;
namespace Singleton
{
public class Program
{
public static void Main(string[] args)
{
var builder = new ModelBuilder();
builder.Configurations.Add(new TemplateConfiguration());
builder.Configurations.Add(new UserConfiguration());
builder.Configurations.Add(new UnitConfiguration());
builder.Configurations.Add(new AttributeConfiguration());
var model = builder.CreateModel();
using (var context = new SampleDataContext(model))
{
bool updating = true;
if (updating)
{
var units = new List<Unit>
{
new Unit{ Name = "Unit1" },
new Unit{ Name = "Unit2" }
};
units.ForEach(x => { context.Units.Add(x); });
context.SaveChanges();
var templates = new List<Template>
{
new Template{
Name = "Default",
Attributes = new List<Attribute>
{
new Attribute
{
Unit = context.Units.Single( i => i.Name == "Unit1" )
}
}
}
};
templates.ForEach(x =>
{
context.Templates.Add(x);
});
context.SaveChanges();
var users = new List<User>
{
new User
{
Name = "Stacey"
},
new User
{
Name = "Daniel"
},
new User
{
Name = "Derek"
}
};
users.ForEach(x => { context.Users.Add(x); });
context.SaveChanges();
updating = !updating; // stop updating
}
if (!updating)
{
Single.Instance = context.Templates.Single(i => i.Name == "Default");
}
foreach (User user in context.Users)
{
Console.WriteLine(user.Template.Name); // does template requery?
}
}
}
}
public class Template
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Attribute> Attributes { get; set; }
}
public class TemplateConfiguration : EntityConfiguration<Template>
{
public TemplateConfiguration()
{
HasKey(k => k.Id);
Property(k => k.Id).IsIdentity();
Property(k => k.Name);
//// map the collection entity
HasMany(k => k.Attributes).WithRequired()
.Map("template.attributes",
(template, attribute) => new
{
Template = template.Id,
Attribute = attribute.Id
});
MapSingleType(c => new
{
c.Id,
c.Name
}).ToTable("templates");
}
}
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
[StoreGenerated(StoreGeneratedPattern.None)]
public Template Template { get { return Single.Instance; } }
}
public class UserConfiguration : EntityConfiguration<User>
{
public UserConfiguration()
{
HasKey(k => k.Id);
Property(k => k.Id).IsIdentity();
Property(k => k.Name);
MapSingleType(c => new
{
c.Id,
c.Name
}).ToTable("users");
}
}
public class Unit
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class UnitConfiguration : EntityConfiguration<Unit>
{
public UnitConfiguration()
{
HasKey(k => k.Id);
Property(k => k.Id).IsIdentity();
Property(k => k.Name);
MapSingleType(c => new
{
c.Id,
c.Name
}).ToTable("units");
}
}
public class Attribute
{
public virtual int Id { get; set; }
public Unit Unit { get; set; }
}
public class AttributeConfiguration : EntityConfiguration<Attribute>
{
public AttributeConfiguration()
{
HasKey(k => k.Id);
Property(k => k.Id).IsIdentity();
// Initialize the Statistic
HasRequired(k => k.Unit);
// map the data type to the context so that it can be queried.
MapHierarchy(c => new
{
c.Id,
Unit = c.Unit.Id
}).ToTable("attributes");
}
}
public class Single
{
public static Template Instance;
}
public class SampleDataContext : DbContext
{
public SampleDataContext(DbModel model)
: base(model)
{
this.ObjectContext.ContextOptions.LazyLoadingEnabled = true;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public DbSet<Template> Templates { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Unit> Units { get; set; }
public DbSet<Attribute> Attributes { get; set; }
}
}
if I assign Singleton.Instance to a query from the DataContext, and then assign the Singleton.Instance to other objects in my code, will the SQL be run once, or each time it is accessed? Can anyone help me here to see if this pattern is going to save some SQL?
From context.SomeQuery, you're almost certainly just returning some kind of queryable object or other iterable (I'm not sure your example reflects how your EF solution is actually architected, I think some elements were lost for the sake of brevity). So, every time you access this singleton, you're just going to iterate over it (run the query) again.
I recommend you use a simple memoization revolving around 4.0 caching, try this.
The SQL will run each time you access it.
What you'r looking for is some kind of caching strategy.
Have a look at the following thread. I think it will help you: How to make Entity Framework cache some objects

Categories