2

I'm using EF5 code first.

We have a method

LogHistoryTracking(DbEntityEntry entity)

to log changes when SaveChanges is called.

At SaveChanges, we get the changed entities and pass into LogHistoryTracking

var changedEntities = ChangeTracker.Entries().ToList();


But when I access

changedEntity.OriginalValues.PropertyNames

there is no properties for foreign keys object (only foreign key Id - but how can we get the data when there is only id here?).

I also tried to google for a solution, but this issue might be not so popular.

There is this article, but it does not work.

Appreciate any help. Thanks.

Community
  • 1
  • 1
NDC00629
  • 33
  • 4

1 Answers1

-1

If you want to have your entity properties to be accessible you must 'Include' them prior to accessing them. Like in the following example which gets the orders of the first cutomer :

    var orders = context.Customers
   .Include("Orders")
   .First().Orders;

In this example if you do not call .Include("Orders") you will not have Customer.Orders. The same goes if you have foreign key and forget to include the navigation property of the foreign key. This is because the key (the ID) is part of the object and the navigation property is not.

Let us see one real world example :

public class Employee : Entity
{
    public virtual int CompanyUserId { get; set; }
    public virtual CompanyUser CompanyUser { get; set; }
    //... cut out for brevity
}

If you get the employees like this :

var employees = context.Employees;

You will not be able to access employees[0].CompanyUser after context.SaveChanges() because of lazy loading. The connection is disposed after context.SaveChanges(), so no more data fetching.

But if you call :

var employees = context.Employees
           .Include("CompanyUser")
           .ToArray();

You will be able to access employees[0].CompanyUser.SomeProperty right away before context.SaveChanges regardless lazy loading because ToArray() will execute the query and fetch the entities with the "includes".

If you call :

var employees = context.Employees
               .Include("CompanyUser");

Then you will have employee[0].CompanyUser.SomeProperty even after context.SaveChanges() with Lazy Loading because you have told EF to include "CompanyUser" property before executing the query. On execution EF will include the named property.

UPDATE Intercepting DbContext can be done in at least two different ways. First - override SaveChanges() or SaveChangesAsync because it is virtual:

public class MyDbContext : DbContext
{
    public event Action<MyDbContext> SavingChanges = _ => { };

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

Second way without direct override is by hiding the DbContext inside interface like this one (this is from real project) :

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

Third way (somewhat different) is by intercepting the Db calls. Fourth way exists but it depends on what IoC you use. If you use Castle Windsor you can use interceptors. I suppose that with every IoC there is its own way of intercepting this.

Ognyan Dimitrov
  • 6,026
  • 1
  • 48
  • 70
  • Can you tell me more specific, how can I add navigation property into the object? I use EF code first with lazy loading, so I can access the navigation property, but what I'm doing is override method SaveChanges() in DbContext. At this method, we can only view DbEntityEntry object which is general – NDC00629 May 11 '15 at 09:27
  • 1
    Sorry, you might misunderstand my context. I'm not saying I'll LogHistoryTracking by my own code whenever I use SaveChanges() to save something into database. I'm trying to override SaveChanges() so that whenever it's called, it'll auto add new record for HistoryTracking. That's why I cannot specific include what I want into the object, because I don't know for this object what should be included. It's generic. I only have var changedEntities = ChangeTracker.Entries().ToList(); and have to treat these entities like generic. – NDC00629 May 11 '15 at 15:10