I am getting the following error when I attempt to add something to my database.
System.InvalidOperationException: ‘The instance of entity type ‘IngredientCategory’ cannot be tracked because another instance with the key value ‘{Id: 15}’ is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.’
This is the line that throws the error in the view model:
context.Ingredients.Add(newItem);
View Model
public partial class IngredientViewModel: ObservableObject
{
RecipeDBContext context;
private Ingredient _ingredient = new Ingredient();
public Ingredient Ingredient => _ingredient;
[ObservableProperty]
private ObservableCollection<Ingredient> _ingredients;
public List<IngredientCategory> Categories { get; set; }
public IngredientViewModel()
{
context = new RecipeDBContext();
_ingredients = new ObservableCollection<Ingredient>();
UpdateIngredients(context.Ingredients.ToList());
_ingredients.CollectionChanged += this.OnCollectionChanged;
Categories= context.IngredientCategories.Where(p => p.ParentId != null).ToList();
}
public void UpdateIngredients(List<Ingredient> ingredients)
{
Ingredients.Clear();
foreach (Ingredient ingredient in ingredients)
{
Ingredients.Add(ingredient);
}
}
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Ingredient newItem in e.NewItems)
{
context.Ingredients.Add(newItem);
}
}
if (e.OldItems != null)
{
foreach (Ingredient oldItem in e.OldItems)
{
context.Ingredients.Remove(oldItem);
}
}
context.SaveChanges();
}
}
Xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentDialog
x:Class="RecipeApp.Views.Dialogs.AddIngredientDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RecipeApp.Views.Dialogs"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:models="using:RecipeApp.Models" d:DataContext="{d:DesignInstance Type=models:Supplier}"
xmlns:viewModels="using:RecipeApp.ViewModels"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Title="Save your work?"
PrimaryButtonText="Save"
CloseButtonText="Cancel"
DefaultButton="Primary"
Width="1000"
Height="1000">
<ContentDialog.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/FormUI.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBox" x:Key="ValidatedTextBox2">
<Setter Property="FontSize"
Value="50" />
</Style>
</ResourceDictionary>
</ContentDialog.Resources>
<StackPanel>
<TextBox x:Name="NameTextBox" Header="Name" PlaceholderText="Enter Name" PlaceholderForeground="Gray" IsColorFontEnabled="True" Text="{x:Bind ViewModel.Ingredient.Name, Mode=TwoWay}" IsSpellCheckEnabled="True"/>
<TextBox x:Name="DescTextBox" TextWrapping="Wrap" AcceptsReturn="True" SelectionHighlightColor="Green" MinWidth="400" Width="600" Height="300" IsSpellCheckEnabled="True" Text="{x:Bind ViewModel.Ingredient.Description, Mode=TwoWay}"/>
<ComboBox x:Name="ingredientCategoryBox" ItemsSource="{x:Bind ViewModel.Categories}" SelectionChanged="ingredientCategoryBox_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="models:IngredientCategory">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel Visibility="{x:Bind ViewModel.Ingredient.HasErrors, Mode=OneWay}" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Vertical" Margin="0 50 0 0">
<TextBlock FontSize="20">Errors:</TextBlock>
<Border Background="Gray">
<TextBlock x:Name="ErrorsBox" Text="{x:Bind ViewModel.Ingredient.Errors, Mode=OneWay}" Foreground="White"/>
</Border>
</StackPanel>
</StackPanel>
</ContentDialog>
Code behind file
public sealed partial class AddIngredientDialog : ContentDialog
{
public IngredientViewModel ViewModel { get; } = new();
public AddIngredientDialog()
{
this.InitializeComponent();
}
private void ingredientCategoryBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ViewModel.Ingredient.Category = ingredientCategoryBox.SelectedItem as IngredientCategory;
}
}
Category Model
public class IngredientCategory
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public IngredientCategory Parent { get; set; }
public ICollection<Ingredient> Ingredients { get; set; }
public Ingredient IngredientId { get; set; }
public IngredientCategory() {}
public IngredientCategory(int id, string name, int parentId)
{
Id = id;
Name = name;
ParentId = parentId;
}
}
Ingredient Model
[Index(nameof(Name), IsUnique = true)]
public partial class Ingredient: BaseModel
{
public int Id { get; set; }
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "Name is Required")]
[MinLength(2, ErrorMessage = "Name should be longer than one character")]
private string _name;
[ObservableProperty]
private string _description;
[ObservableProperty]
private int _categoryId;
public IngredientCategory Category { get; set; }
//public Ingredient(string name, IngredientCategory category)
public Ingredient()
{
SetProperty(ref _name, _name, true, nameof(Name));
}
/*
~Ingredient()
{
ErrorsChanged -= Errors_Changed;
PropertyChanged -= Property_Changed;
}
*/
public Ingredient(string name, string description, IngredientCategory category)
{
if (category == null)
{
Debug.WriteLine("category was null for " + name);
}
else
{
Name = name;
Description = description;
CategoryId = category.Id;
//Category = category;
}
}
}