why ef lost relationship once SaveChanges?

If I simply do this:

var medical = ctx.Medicals.FirstOrDefault(p => p.ID == medicalViewModel.ID);
var sizeClinics = medical.Clinics.Count;

The amount is (for example) 10 (i.e. I have 10 clinics for that medical). Now, if I do this:

var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();

medical = ctx.Medicals.FirstOrDefault(p => p.ID == medicalViewModel.ID);
var sizeClinics = medical.Clinics.Count;

The size is 0. Why? It seems it remove relationship after SaveChanges?

Here’s the Medicals object:

public partial class Medicals
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Medicals()
    {
        this.Activities = new HashSet<Activities>();
        this.MedicalsRefunds = new HashSet<MedicalsRefunds>();
        this.Clinics = new HashSet<Clinics>();
    }

    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Activities> Activities { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<MedicalsRefunds> MedicalsRefunds { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Clinics> Clinics { get; set; }
}

I thing I’ve noticed: if I analyze medical object with QuickWatch the first time (without SaveChanges part) its as {System.Data.Entity.DynamicProxies.Medicals_650D310387E78A83885649345ED0FB2870EC304BF647B59321DFA0E4FBC78047}.

Instead, if I do SaveChanges and then I retrieve that medical, it is as {MyNamespace.Models.Medicals}.

What can it be?

Answer

This question is answered by understanding how Entity Framework works internally. I’ll try to highlight the key features here.

Change tracking

Entity Framework has a sort of cache of entities in-memory, called the change tracker.

In your first example, when you fetch an entity from the database:

var medical = ctx.Medicals.FirstOrDefault(p => p.ID == medicalViewModel.ID);

Entity Framework creates the Medicals instance that you receive. When it does so, it also uses that opportunity to store a reference to that object, for its own reasons. It will keep an eye on those objects and track any changes made to them.

For example, if you now call ctx.SaveChanges(); at any point, it’s going to look at everything in its change tracker, see which things have been changed, and update those in the database.

There are several benefits attached to this: you don’t have to explicitly tell EF that you made changes to some of the entities it was already tracking in its cache, and EF can also spot which specific fields have changed, so it only has to update those specific fields and it can ignore the unchanged fields.

Update from comments: EF only allows the tracking of one instance of a given entity, based on the PK value. So if you’ve already tracked the Medical with ID 123, you can’t track another instance of the same Medical entity with ID 123.

Lazy loading

The code you use suggests that you are lazy loading. I’m going to gloss over the intricate details here, to keep it simple. If you don’t know what lazy/eager loading is, I suggest you look this up, as the explanation is too long to write down here. Lazy/eager loading is a key concept in Entity Framework for dealing with entity relations and how to fetch related entities.

When dealing with lazy loading, EF slightly tinkers with your entity when it fetches it for you. It puts a special lazy collection in all the entity’s navigational properties (such as medical.Clinics), so that it will fetch the related data only when you actually try to access it, i.e. by enumerating the collection in any way.

Comparatively, if you were using eager loading, EF wouldn’t do this for you and the nav prop simply wouldn’t be filled in with anything unless you explicitly called Include on it.

Updating untracked entities

In your second example, you are working with an entity object which was not created by Entity Framework. You made it yourself:

 var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);

And now you manually add it to the change tracker:

ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;

There’s nothing wrong with this, but you have to realize that the entity in the change tracker was not generated by EF, and therefore it doesn’t contain these special “lazy navigational properties”. And because it doesn’t contain these lazy navigational properties…

var sizeClinics = medical.Clinics.Count;

… the above code doesn’t actually try to fetch the data from the database. It simply works with the entity object you generated and what it already contains in-memory.

And since you didn’t add anything to medical.Clinics yourself, the collection is therefore empty.

The answer

Lazy loading only works on entity objects generated by EF, not on entity objects generated by you, regardless of whether you manually added it to EF’s change tracker afterwards or not.

So to get the count, you can specifically query the clinics from the database:

var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
var clinicCount = ctx.Clinics.Count(p => p.MedicalId == medical.ID);

Or you could detach the entity and fetch it from the db, though I’m not a fan of this:

var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();

// Detach
ctx.Entry(medical).State = EntityState.Detached;

// Now fetch from db
var medical2 = ctx.Medicals.FirstOrDefault(p => p.ID == medical.ID);
var sizeClinics = medical2.Clinics.Count;

Why detach? Remember how I mentioned that EF only allows tracking of one entity of a given type and PK. Since the object referred to by medical is already being tracked, you can’t fetch and track another new instance of Medicals with the same PK.
By detaching the first, medical2 can be fetched and tracked since the change tracker “forgot” the other instance.

But to be honest, it would be easier to just open a new context instead of trying to manually detach and re-query.

var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();

using(var ctx2 = new MyContext())
{
    var medical2 = ctx2.Medicals.FirstOrDefault(p => p.ID == medical.ID);
    var sizeClinics = medical2.Clinics.Count;
}

More info if you’re interested

If you’re using code first, lazy loading is why EF requires you to make these properties virtual. EF needs to be able to inherit from your entity class and make a special derived class which overrides the navigational property behavior.

You already stumbled on this, when you said:

I thing I’ve noticed: if I analyze medical object with QuickWatch the first time (without SaveChanges part) its as {System.Data.Entity.DynamicProxies.Medicals_650D310387E78A83885649345ED0FB2870EC304BF647B59321DFA0E4FBC78047}.

Instead, if I do SaveChanges and then I retrieve that medical, it is as {MyNamespace.Models.Medicals}.

That System.Data.Entity.DynamicProxies.Medicals_65 (and so on) class was dynamically generated by Entity Framework, inherits the Medicals class, and overrides the virtual navigational properties so that it lazily loads this information when the collection is enumerated.

This is the hidden magic of how EF achieves lazy loading.