I would like to expand the question asked on this thread
Binding listbox to observablecollection
by giving it an ability to persistent the data. The structure is mostly the same except I installed Entity Framework Core, created a DbContext class to hold the records. I added a button to save the dataset to SQL Server. I did not encounter the compiling error but when I attempted to save the data in the database I got this run time exception:
Message=The entity type 'Fruit' requires a primary key to be defined.
The whole exception in its entirety is listed below
System.InvalidOperationException was unhandled
HResult=-2146233079
Message=The entity type 'Fruit' requires a primary key to be defined.
Source=Microsoft.EntityFrameworkCore
StackTrace:
at Microsoft.EntityFrameworkCore.Internal.ModelValidator.ShowError(String message)
at Microsoft.EntityFrameworkCore.Internal.ModelValidator.EnsureNonNullPrimaryKeys(IModel model)
at Microsoft.EntityFrameworkCore.Internal.ModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Internal.RelationalModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.c__DisplayClass14_0.b__0(Object k)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.LazyRef1.get_Value()2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.c.b__0_6(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactoryService(FactoryService factoryService, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument)2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument)1 accessor)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.c__DisplayClass16_0.b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure
at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.get_StateManager()
at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges()
at Microsoft.EntityFrameworkCore.DbContext.TryDetectChanges()
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
at Fruits.MainWindow.SaveFruitCommandBinding_Executed(Object sender, ExecutedRoutedEventArgs e) in D:\Frank\Test\Fruits\Fruits\MainWindow.xaml.cs:line 50
at System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
at System.Windows.Input.CommandManager.ExecuteCommandBinding(Object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
at System.Windows.Input.CommandManager.FindCommandBinding(CommandBindingCollection commandBindings, Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
at System.Windows.Input.CommandManager.FindCommandBinding(Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
at System.Windows.Input.CommandManager.OnExecuted(Object sender, ExecutedRoutedEventArgs e) at System.Windows.UIElement.OnExecutedThunk(Object sender, ExecutedRoutedEventArgs e) at System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(Delegate genericHandler, Object target) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted) at System.Windows.Input.RoutedCommand.ExecuteImpl(Object parameter, IInputElement target, Boolean userInitiated) at System.Windows.Input.RoutedCommand.ExecuteCore(Object parameter, IInputElement target, Boolean userInitiated) at MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(ICommandSource commandSource, Boolean userInitiated) at System.Windows.Controls.Primitives.ButtonBase.OnClick() at System.Windows.Controls.Button.OnClick() at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e) at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent) at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args) at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted) at System.Windows.Input.InputManager.ProcessStagingArea() at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input) at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport) at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel) at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) at System.Windows.Application.RunDispatcher(Object ignore) at System.Windows.Application.RunInternal(Window window) at System.Windows.Application.Run(Window window) at System.Windows.Application.Run() at Fruits.App.Main() at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException:
This is the updated class Fruit:
namespace Fruits.ViewModels { [Table("Fruits")] public class Fruit : ViewModelBase { #region Constractor public Fruit() { } public Fruit(string name, String clrString) { FruitName = name; // Parse colors like so: (Color)ColorConverter.ConvertFromString(clrString); FruitColor = clrString; _id = Guid.NewGuid(); } public Fruit(string name, Color clr) { FruitName = name; FruitColor = clr.ToString(); _id = Guid.NewGuid(); } #endregion #region Properties private Guid _id; [Key] public Guid ID { get { return _id; } } #region FruitName private string _fruitname; public string FruitName { get { return _fruitname; } set { if (_fruitname != value) { _fruitname = value; OnPropertyChanged("FruitName"); } } } #endregion #region FruitColor private String _fruitcolor; public String FruitColor { get { return _fruitcolor; } set { if (_fruitcolor != value) { _fruitcolor = value; OnPropertyChanged("FruitColor"); } } } #endregion #region Selected Property private bool _isSelected = true; // NOTE: I renamed this property public bool IsSelected { get { return _isSelected; } set { if (_isSelected != value) { _isSelected = value; OnPropertyChanged("IsSelected"); } } } #endregion #endregion } } The updated MainWindows xaml (to add a save button)
Selected Fruit Only and my code behind the mainwindows (added handler)
using Fruits.ViewModels; using System; using System.Windows; using System.Windows.Input; namespace Fruits { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); ViewModel.AddNewFruit("Jackfruit", "Yellow"); ViewModel.AddNewFruit("Watermelon", "ForestGreen"); ViewModel.AddNewFruit("Apple", "Red"); ViewModel.AddNewFruit("Banana", "Yellow"); ViewModel.AddNewFruit("Orange", "DeepSkyBlue"); //ViewModel.Fruits[0].IsSelected = false; //ViewModel.Fruits[1].IsSelected = false; ViewModel.FruitsView.Refresh(); } public MainViewModel ViewModel { get { return DataContext as MainViewModel; } } private void AddFruitCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { ViewModel.AddNewFruit(); } private void AddFruitCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = ViewModel != null && !String.IsNullOrWhiteSpace(ViewModel.NewFruitName) && !String.IsNullOrWhiteSpace(ViewModel.NewFruitColor) ; } private void SaveFruitCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { using (var db=new FruitDbContext()) { db.SaveChanges(); } } private void SaveFruitCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } } } My newly added dbContext:
namespace Fruits.ViewModels { public class FruitDbContext:DbContext { public DbSet Fruits { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder) { optionBuilder.UseSqlServer(@"Server = xxx; Database=Test; Integrated Security = True"); } } } Other classes remain unchanged, but I listed them anyway:
ViewModelBase
namespace Fruits.ViewModels { public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } } ViewModel using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Data; using System.Windows.Media; namespace Fruits.ViewModels { #region MainViewModel Class public class MainViewModel : ViewModelBase { public MainViewModel() { Fruits = new ObservableCollection(); } public ICollectionView FruitsView { get; private set; } #region ShowSelectedFruitOnly Property private bool _showSelectedFruitOnly = true; public bool ShowSelectedFruitOnly { get { return _showSelectedFruitOnly; } set { if (value != _showSelectedFruitOnly) { _showSelectedFruitOnly = value; FruitsView.Refresh(); OnPropertyChanged("ShowSelectedFruitOnly"); } } } #endregion ShowSelectedFruitOnly Property #region Add Methods public void AddNewFruit() { Fruits.Add(new Fruit(NewFruitName, NewFruitColor)); NewFruitName = ""; NewFruitColor = ""; } public void AddNewFruit(string name, string color) { Fruits.Add(new Fruit(name, color)); } public void AddNewFruit(string name, Color color) { Fruits.Add(new Fruit(name, color)); } #endregion Add Methods #region NewFruitName Property private String _newFruitName = default(String); public String NewFruitName { get { return _newFruitName; } set { if (value != _newFruitName) { _newFruitName = value; OnPropertyChanged("NewFruitName"); } } } #endregion NewFruitName Property #region NewFruitColor Property private String _newFruitColor = default(String); public String NewFruitColor { get { return _newFruitColor; } set { if (value != _newFruitColor) { _newFruitColor = value; OnPropertyChanged("NewFruitColor"); } } } #endregion NewFruitColor Property #region Fruits Property private static ObservableCollection _fruits; public ObservableCollection Fruits { get { return _fruits; } private set { if (value != _fruits) { _fruits = value; FruitsView = CollectionViewSource.GetDefaultView(Fruits); FruitsView.Filter = FruitFilterPredicate; FruitsView.Refresh(); OnPropertyChanged("Fruits"); } } } protected bool FruitFilterPredicate(Object o) { if (ShowSelectedFruitOnly) { return (o as Fruit).IsSelected; } return true; } #endregion Fruits Property } #endregion MainViewModel Class } App.xaml
The structure of my project
My table in SQL Server:
CREATE TABLE [dbo].[Fruits] ( [ID] [uniqueidentifier] NOT NULL, [FruitName] [nvarchar](50) NULL, [FruitColor] [nvarchar](50) NULL, [IsSelected] [nvarchar](1) NULL, CONSTRAINT [PK_Fruit] PRIMARY KEY CLUSTERED ([ID] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] Please advise why the message said there was no primary key while it does exist
