none
Attach an entity to the DbContext that was retrieved by AsNoTracking RRS feed

  • Question

  • Hi,

    I am using Entity Framework 4.1, Database first and the DbContext API.
    In my MVVM application I am binding directly to my Entity Framework entities. There are two entities: Employee and Organisation having a one-to-many relationship (Employee has a navigation property to a single organisation).

    To change an existing employee my UI contains a combobox to select the organisation the employee belongs to. For performance reasons the Organisation entities are retrieved using the AsNoTracking method. The employee is being tracked by the DbContext.

    Note that the the employee's Organisation property is bound to the SelectedValue of the ComboBox. So when the user selects an organisation from the ComboBox the employee's Organisation property is set to the selected organisation, but the Organisation entity is not being tracked by the context.

    When I want to save the employee to the database I try to attach the selected Organisation entity to the DbContext in different ways:

    this.dbContext.Organisations.Attach(organisation);

    this.dbContext.Entry(organisation).State = EntityState.Unchanged;

    I get the following exception:

    "The object in the 'Organisation' role cannot be automatically added to the context because it was retrieved using the NoTracking merge option. Explicitly attach the entity to the ObjectContext before defining the relationship."

    How do I attach an entity to the context that was being retrieved using the AsNoTracking method?

    Help is appreciated.

    Michel Miranda


    Friday, June 24, 2011 5:48 AM

Answers

  • Michel,


    A possible work around for this scenario is to disable proxies while doing the NoTracking query. For example, something like this:


    ·         context.Configuration.ProxyCreationEnabled = false;

    ·         var department = context.Departments.AsNoTracking().FirstOrDefault();

    ·         context.Configuration.ProxyCreationEnabled = true;

     You won’t get a proxy or any lazy loading, but that’s probably OK for this scenario. I don’t think the exception will happen anymore because we will have no way of knowing that the entity was obtained using NoTracking.

    Do you see a huge performance benefit when doing NoTracking in this scenario? If yes, could you please email me. One of the developers would like to see the numbers. 

    Thank you,

    Julia


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Tuesday, June 28, 2011 3:01 AM
    Moderator

All replies

  • Hi Michel,

    when I do the following, the Detached department gets attached successfully.

    When you call attach the entities are attached to the context in the Unchanged state 

     

    var department = context.Departments.AsNoTracking().FirstOrDefault();
    var course = context.Courses.Where(c => c.DepartmentID == department.DepartmentID).FirstOrDefault();
    context.Departments.Attach(department);

     

    Maybe you could send your model and the code.

     

    Thanks,

    Julia


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, June 24, 2011 7:38 AM
    Moderator
  • Hi Julia,

    Thanks for your response.

    In my situation as a result of the WPF binding to the SelectedValue of the ComboBox  the course's Department property is set before attaching it to the context. Could that be the problem?

    Thanks,

    Michel

    Friday, June 24, 2011 7:49 AM
  • Michel,

    I am not sure, can you send me your code to juliako@microsoft.com

     

    Thank you,

    Julia


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Saturday, June 25, 2011 7:39 AM
    Moderator
  • Hi Julia,

    Thank you for your time.

    I have implemented a simple stripped sample (not production code ;-) ). My code contains Dutch words, sorry for that. There are two entities. The RelatieDochter entity is the one we are editing in the WPF window. This entity exposes a Dochter property referencing a Relatie entity. A RelatieDochter entity has one Relatie entity (Dochter property). A Relatie entity has multiple RelatieDochter entities.

    All possible Relatie entities are presented by a ComboBox. For performance reasons these entities are retrieved as AsNoTracking.

    Entities is the name of my DBContext class.

    Here is my ViewModel:

    public class ViewModel : INotifyPropertyChanged
    {
      #region INotifyPropertyChanged Members
    
      public event PropertyChangedEventHandler PropertyChanged;
    
      protected void RaisePropertyChanged(string propertyName)
      {
        if (this.PropertyChanged != null)
        {
          this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    
      #endregion
    
      private Entities dbContext;
    
      private List<Relatie> relaties;
      public List<Relatie> Relaties
      {
        get { return this.relaties; }
        set
        {
          this.relaties = value;
          RaisePropertyChanged("Relaties");
        }
      }
    
      private RelatieDochter currentRelatieDochter;
      public RelatieDochter CurrentRelatieDochter
      {
        get { return this.currentRelatieDochter; }
        set
        {
          this.currentRelatieDochter = value;
          RaisePropertyChanged("CurrentRelatieDochter");
        }
      }
    
      public ViewModel()
      {
        this.dbContext = new Entities();
        dbContext.Configuration.LazyLoadingEnabled = false;
    
        CreateModel();
      }
    
      private void CreateModel()
      {
        // Retrieve ComboBox items using AsNoTracking
        this.Relaties = this.dbContext.Relaties.Include(r => r.Vestigingsadres).OrderBy(r => r.BedrijfsNaam).AsNoTracking().ToList();
        // Retrieve the entity to edit
        this.CurrentRelatieDochter = this.dbContext.RelatieDochters.Include(r => r.Dochter).Where(r => r.ID == 1).First();
      }
    
      public void SaveModel()
      {
        this.dbContext.Relaties.Attach(this.CurrentRelatieDochter.Dochter);
        this.dbContext.SaveChanges();
      }
    }
    

    My WPF window:

    <Window x:Class="UserInterface.EFTest.Window1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Window1" Height="300" Width="300">
      <Grid DataContext="{Binding CurrentRelatieDochter}">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="Auto" />
          <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Naam" />
        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ID, Mode=TwoWay}" />
    
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Dochter" />
        <ComboBox Grid.Row="1" Grid.Column="1" 
         ItemsSource="{Binding DataContext.Relaties, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
         SelectedValue="{Binding Dochter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" 
         DisplayMemberPath="BedrijfsNaam" />
    
        <Button Grid.Row="2" Grid.Column="0" Content="Save" Click="OnSaveClick" />
      </Grid>
    </Window>
    

    Code behind:

    public partial class Window1 : Window
    {
      ViewModel viewModel = new ViewModel();
    
      public Window1()
      {
        InitializeComponent();
    
        this.viewModel = new ViewModel();
        this.DataContext = viewModel;
      }
    
      private void OnSaveClick(object sender, RoutedEventArgs e)
      {
        this.viewModel.SaveModel();
      }
    }
    

    The window displays the RelatieDochter entity for editing. It contains a ComboBox to select the Relatie entity.

    When I select an item from the ComboBox and hit save I need to attach the disconnected Relatie to the context before saving. But then I get the following exception:

    "The object in the 'Relatie' role cannot be automatically added to the context because it was retrieved using the NoTracking merge option. Explicitly attach the entity to the ObjectContext before defining the relationship."

    Thanks,

    Michel Miranda


     

    Saturday, June 25, 2011 12:19 PM
  • Hi Julia,

    Without reproducing my sample, I get the same exception with the following code block:

    // Retrieve Relatie entity using AsNoTracking
    Relatie relatie = this.dbContext.Relaties.AsNoTracking().First();
    
    // Retrieve RelatieDochter entity to change
    RelatieDochter relatieDochter = this.dbContext.RelatieDochters.Include(r => r.Dochter).Where(r => r.ID == 1).First();
          
    // Update foreign key
    relatieDochter.Dochter = relatie;
    
    // Try to attach the foreign key entity before saving
    this.dbContext.Relaties.Attach(relatie);
    
    // Save
    this.dbContext.SaveChanges();
    

    Due to the XAML bindings the foreign key property is set before this entity is attached to the context resulting in the exception.

    Help is appreciated.

    Thanks,

    Michel Metselaar

    Saturday, June 25, 2011 5:22 PM
  • Hello,

    I'm agreed with first reply from Julia. It looks that your question is related to the order from your last reply. I suggest you can try to place this.dbContext.Relaties.Attach(relatie) before you update relationship and try it again. Please check here for more information about Attaching Entities. http://msdn.microsoft.com/en-us/library/gg696174(v=VS.103).aspx

    In addition, have you sent your code demo to Julia and any update? If not, you can also send it to me v-xugong@microsoft.com I'm willing to helping you test your code on my side.

    If you have any finding, please feel free to let us know.

    Best Regards,


    Larcolais Gong[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, June 27, 2011 5:56 AM
  • Hi Larcolais,

    Thanks for your response.

    It is related to the order of the statements indeed. That is the whole point. But I cannot change the order in my case.

    It think it is not necessary to send the code including the database since the problem can be very easily described.

    This works:

    var department = context.Departments.AsNoTracking().FirstOrDefault();
    var course = context.Courses.FirstOrDefault();
    
    context.Departments.Attach(department);
    course.Department = department;
    
    context.SaveChanges();
    
    

    This doesn't work (order changed):

    var department = context.Departments.AsNoTracking().FirstOrDefault();
    var course = context.Courses.FirstOrDefault();
    
    course.Department = department;
    context.Departments.Attach(department);
    
    context.SaveChanges();
    
    

    In my situation the related Department entity is bound to a ComboBox containing departments that are not being tracked. So when the user selects a new department from the ComboBox it isn't yet attached to the context. I have to do that afterwards. But that doesn't work.

    Maybe there is another way to retrieve the list of departments. I need them to be retrieved totally detached and attach the new department to the context after changing the foreign key property.

    Thanks,

    Michel Miranda


    Monday, June 27, 2011 7:45 AM
  • Michel,


    A possible work around for this scenario is to disable proxies while doing the NoTracking query. For example, something like this:


    ·         context.Configuration.ProxyCreationEnabled = false;

    ·         var department = context.Departments.AsNoTracking().FirstOrDefault();

    ·         context.Configuration.ProxyCreationEnabled = true;

     You won’t get a proxy or any lazy loading, but that’s probably OK for this scenario. I don’t think the exception will happen anymore because we will have no way of knowing that the entity was obtained using NoTracking.

    Do you see a huge performance benefit when doing NoTracking in this scenario? If yes, could you please email me. One of the developers would like to see the numbers. 

    Thank you,

    Julia


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Tuesday, June 28, 2011 3:01 AM
    Moderator
  • Hi Julia,

    Thank you! My sample code is working now.

    Still having a small problem in my production code when retrieving the departments using the Include method:

    context.Configuration.ProxyCreationEnabled = false;
    var department = context.Departments.AsNoTracking().Include(d => d.RelatedEntity).FirstOrDefault();
    context.Configuration.ProxyCreationEnabled = true;
    

    In the ChangeTracker I see that the state of the related entity is Added instead of Unchanged resulting in an exception. When I change the state of the related entity to Unchanged before saving it is working:

    context.Entry(department.RelatedEntity).State = EntityState.Unchanged;<br/>
    

    Is there a cleaner solution to retrieve the related entity as Unchanged?

    I will email you about my performance benefit.

    Thanks,

    Michel Miranda

    Tuesday, June 28, 2011 5:18 AM
  • after the changes are saved (SaveChanges), the entity state changes to unchanged automatically. Check here: http://msdn.microsoft.com/en-us/library/gg696174(v=VS.103).aspx

    Thanks,

    Esters


    Just a newbie for everything.
    Wednesday, June 29, 2011 10:49 AM
  • Hi Esters,

    Please read my latest post carefully. Calling SaveChanges gives me an exception.

    context.Configuration.ProxyCreationEnabled = false;
    var department = context.Departments.AsNoTracking().Include(d => d.RelatedEntity).FirstOrDefault();
    context.Configuration.ProxyCreationEnabled = true;
    

    I only need to know why the state of the RelatedEntity's is Added instead of Unchanged and how to cope with it. Is it a bug?

    Thanks,

    michel

    Wednesday, June 29, 2011 11:09 AM
  • Michel sorry for my mistake. IMHO, Added is occurred the object is new, has been added to context. after the changes are saved, the object state changes to unchanged. http://msdn.microsoft.com/en-us/library/bb896269.aspx But i didnt see anything was related to your scenario. Anything was overlooked by me?

    Thanks,

    Esters,


    Just a newbie for everything.
    Thursday, June 30, 2011 9:34 AM
  • Hi Julia,

    Thank you for your help. I appreciate it.

    I will close this thread. Still having some problems specific to my scenario. I created a new thread describing my problem:

    http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/e52bb7e1-e67f-43dd-b0e6-d95ef6400a1b

    Thanks,

    Michel Miranda

    Sunday, July 17, 2011 1:54 PM