3

Using C#, I've generated my DB model using "Generate from Database". The POCO classes and context are generated with T4 templates. everything is working fine and the app is able to edit, insert etc. except I cannot override the SaveChanges method in my entities class. I need to do this to add buisiness logic. Here is the context class:

//------------------------------------------------------------------------------
// <auto-generated>
//    This code was generated from a template.
//
//    Manual changes to this file may cause unexpected behavior in your application.
//    Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace WebApplication1
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;

   public partial class IInvoiceEntities2 : DbContext
   {
        public IInvoiceEntities2 ()
            : base("name=IInvoiceEntities2 ")
        {
        }        


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }






    public DbSet<Company> Companies { get; set; }
    public DbSet<CompanyDetail> CompanyDetails { get; set; }
    public DbSet<CompanyVersion> CompanyVersions { get; set; }
    public DbSet<CustomerDetail> CustomerDetails { get; set; }
    }
}

Any ideas why my SaveChanges method isn't being hit when I set a breakpoint in it and edit an entity?

Update:

I now override the ValidateEntity method in my context class as well as SaveChanges, but when I edit an entity and set a breakpoint in SaveChanges or ValidateEntity, neither methods are being called (see code above)

Update 2:

I've now created a partial class in App_Code folder for the SaveChanges and ValidateEntity, but these methods are still not being executed :

namespace WebApplication1
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

public partial class IInvoiceEntities2 : DbContext
{
    public IInvoiceEntities2 ()
        : base("name=IInvoiceEntities2 ")
    {
    }


    public override int SaveChanges()
    {
        return base.SaveChanges();
    }




protected override DbEntityValidationResult ValidateEntity(
System.Data.Entity.Infrastructure.DbEntityEntry entityEntry,
IDictionary<object, object> items)
{
  // do stuff

    if (result.ValidationErrors.Count > 0)
    {
        return result;
    }
    else
    {
        return base.ValidateEntity(entityEntry, items);
    }
}





}

}

DeadlyDan
  • 669
  • 2
  • 8
  • 20
  • 1
    The obvious answer would be because editing an entity doesn't cause any changes to be saved. What does the code that actually causes the changes to be saved look like? –  Dec 01 '14 at 21:48
  • I'm using a dynamic data entities web app, so its a generic edit.aspx page template. – DeadlyDan Dec 01 '14 at 21:51
  • 1
    Where and when do you call `SaveChanges()`? – Gert Arnold Dec 01 '14 at 22:44
  • I have an entity called CustomerDetails. When I insert a new CustomerDetails entity using the insert.aspx page template, I want to check that a customer with the same name doesn't exist for the same country - Country is a foreign key in CustomerDetails . thanks in advance :> – DeadlyDan Dec 01 '14 at 23:00
  • You should override the `ValidateEntity` method to check for duplicates rather than `SaveChanges` http://stackoverflow.com/a/18736484/150342 but if you want to know the answer to your original question you should show us where you call `SaveChanges()` as @GertArnold and @hvd both suggested – Colin Dec 02 '14 at 09:16
  • I now override the ValidateEntity method in my context class as well as SaveChanges, but when I edit an entity and set a breakpoint in SaveChanges or ValidateEntity, neither methods are being called (see code above)- either of these methods would do me fine but neither are working for me - my model was created using "Generate from Database" – DeadlyDan Dec 02 '14 at 10:14
  • Why are you writing code in an auto-generated file? Use another partial to override the methods you need. – haim770 Dec 02 '14 at 12:11
  • ATM I know I that the methods should be in a partial class because they will get overwritten if I regenerate the model from DB - i can fix this later. But my big problem is that nether of these overriden methods are being executed? – DeadlyDan Dec 02 '14 at 12:14
  • I've now created a partial class for the IInvoiceEntities2 class. – DeadlyDan Dec 02 '14 at 12:23
  • 1
    Dynamic Data uses Data Binding and the entity Data Source so the save changes is called inside the data source. – Wizzard Dec 02 '14 at 12:35
  • Thanks for this Wizzard, is there "any way" to add business logic to Dynamic Data before a record is modified/added/deleted? – DeadlyDan Dec 02 '14 at 12:39
  • I know I can create a custom page for each CRUD action and customise each method. But if I can avoid making custom pages just to add business logic then this is best. Thanks for the help:) – DeadlyDan Dec 02 '14 at 12:48

3 Answers3

5

you can use this partial class style if you want to override saving changes, it should be noted if the methods here are not called it's usually an indicator that partial class is not matching the actual class, check namespace etc.

public partial class MyEntities : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            SavingChanges();
            return base.SaveChanges();
        }
        catch (Exception exception)
        {
            //handle errors here
        }
    }

    private void SavingChanges()
    {
        using (var OC = new MyEntities())
        {
            var objects = this.ChangeTracker.Entries()
                .Where(p => p.State == EntityState.Added || 
                    p.State == EntityState.Deleted || 
                    p.State == EntityState.Modified);

            // handle auditing
            AuditingHelperUtility.ProcessAuditFields(
                objects.Where(p => p.State == EntityState.Added));
            AuditingHelperUtility.ProcessAuditFields(
                objects.Where(p => p.State == EntityState.Modified), InsertMode: false);

            // Inserted objects
            foreach (DbEntityEntry entry in objects
                .Where(p => p.State == EntityState.Added))
            {
                if (entry.Entity != null)
                {
                    // insert code 
                }
            }

            // Updated objects
            foreach (DbEntityEntry entry in objects
                .Where(p => p.State == EntityState.Modified))
            {
                if (entry.Entity != null)
                {
                    // update code 
                }
            }

            // Delete objects
            foreach (DbEntityEntry entry in objects
                .Where(p => p.State == EntityState.Deleted))
            {
                if (entry.Entity != null)
                {
                    // delete code 
                }
            }
        }
    }
}
Wizzard
  • 924
  • 5
  • 9
  • Yes thanks for this, I've tried this already in a partial class and it doesn't get called. And just to prove that there is nothing wrong with my partial class declarations: I override SaveChanges() in the main entities class it still isn't called. P.S: I'm new to EF so it may be a case that I'm missing an obvious trick here.. – DeadlyDan Dec 02 '14 at 12:59
  • Perhaps you know of a working example of Dynamic Data that I can download that overrides the SaveChanges() and works? – DeadlyDan Dec 02 '14 at 13:09
  • Not sure if this matters but my generated EDMX file is NOT in the App_Code folder, but instead is a child of the project and sits at the same level as global.asax – DeadlyDan Dec 02 '14 at 13:59
  • Still no suitable answer to this problem. Any tips are greatly appreciated :) – DeadlyDan Dec 02 '14 at 17:40
1

I've come up with a solid workaround to this issue since I wasn't able to override SaveChanges(). Instead I implement the OnUpdating event of the EntityDataSource :

<asp:EntityDataSource ID="DetailsDataSource" runat="server" EnableUpdate="true"     
OnUpdating="DetailsDataSource_Updating" />

Then I have this method in my code behind which allows me to do server side validation:

protected void DetailsDataSource_Updating(object sender, EntityDataSourceChangingEventArgs e)
    {
        Country c = (Country) e.Entity;
        if (c.CountryName != "North pole")
            e.Cancel = true;
    }

I wish I could override save changes but this will have to do for now. Thanks everyone for your help.

DeadlyDan
  • 669
  • 2
  • 8
  • 20
0

NOTE I've only tested this in EF6 not EF5

The way to do it that covers off all scenarios (including EntityDataSource) is to add a handler to the SavingChanges event of the underlying ObjectContext. This can be done from your DbContext constructor :

 var objCx = (this as IObjectContextAdapter).ObjectContext;
 objCx.SavingChanges += (s, e) => { .... }
LMK
  • 1,496
  • 1
  • 13
  • 15