Vorrei espandere la domanda posta su questo thread
Elenco di rilegatura per osservare la raccolta
dandogli la possibilità di persistere i dati. La struttura è quasi sempre la stessa, tranne che ho installato Entity Framework Core, creato una classe DbContext
per contenere i record. Ho aggiunto un pulsante per salvare il set di dati su SQL Server. Non ho riscontrato l'errore di compilazione ma quando ho tentato di salvare i dati nel database ho ottenuto questa eccezione di run time:
Messaggio = Il tipo di entità 'Frutta' richiede una chiave primaria da definire.
L'intera eccezione nella sua interezza è elencata di seguito
System.InvalidOperationException non è stato gestito
HResult = -2.146,233079 millions
Messaggio = Il tipo di entità 'Frutta' richiede una chiave primaria da definire.
Fonte = Microsoft.EntityFrameworkCore
StackTrace:
a Microsoft.EntityFrameworkCore.Internal.ModelValidator.ShowError (String message)
su Microsoft.EntityFrameworkCore.Internal.ModelValidator.EnsureNonNullPrimaryKeys (modello IModel)
su Microsoft.EntityFrameworkCore.Internal.ModelValidator.Validate (modello IModel)
su Microsoft.EntityFrameworkCore.Internal.RelationalModelValidator.Validate (modello IModel)
su Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel (contesto DbContext, convenzione IConventionSetBuilderSetBuilder, validatore IModelValidator)
a Microsoft.EntityFrameworkCore.Infrastructure.ModelSource. <> c__DisplayClass14_0.b__0 (Oggetto k)
a System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func
2 valueFactory)
su Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel (contesto DbContext, convenzione IConventionSetBuilderSetBuilder, validatore IModelValidator)
su Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel ()
a Microsoft.EntityFrameworkCore.Internal.LazyRef1.get_Value()
2.VisitCallSite (callSite, argomento TArgument di IServiceCallSite)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.<>c.<AddEntityFramework>b__0_6(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactoryService(FactoryService factoryService, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
su Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped (ScopedCallSite scopeedCallSite, provider ServiceProvider)
su Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
2.VisitCallSite (callSite, argomento TArgument di IServiceCallSite)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
su Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped (ScopedCallSite scopeedCallSite, provider ServiceProvider)
su Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
1 accessor)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>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
su Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.get_StateManager ()
su Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges ()
a Microsoft.EntityFrameworkCore.DbContext.TryDetectChanges ()
a Microsoft.EntityFrameworkCore.DbContext.SaveChanges (Boolean acceptAllChangesOnSuccess)
su Microsoft.EntityFrameworkCore.DbContext.SaveChanges ()
at Fruits.MainWindow.SaveFruitCommandBinding_Executed (Object sender, ExecutedRoutedEventArgs e) in D: \ Frank \ Test \ Fruits \ Fruits \ MainWindow.xaml.cs: riga 50
a System.Windows.Input.CommandBinding.OnExecuted (oggetto mittente, ExecutedRoutedEventArgs e)
a System.Windows.Input.CommandManager.ExecuteCommandBinding (oggetto sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
a System.Windows.Input.CommandManager.FindCommandBinding (CommandBindingCollection commandBindings, Object sender, RoutedEventArgs e, comando ICommand, Boolean execute)
a System.Windows.Input.CommandManager.FindCommandBinding (mittente dell'oggetto, RoutedEventArgs e, comando ICommand, esecuzione booleana)
a System.Windows.Input.CommandManager.OnExecuted (Object sender, ExecutedRoutedEventArgs e) su System.Windows.UIElement.OnExecutedThunk (Object sender, ExecutedRoutedEventArgs e) su System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler (Delegate genericHandler, Object target) su System.Windows.RoutedEventArgs.InvokeHandler (Delegate handler, Object target) su System.Windows.RoutedEventHandlerInfo.InvokeHandler (Object target, RoutedEventArgs routedEventArgs) su System.Windows.EventRoute.InvokeHandlersImpl (Object source, RoutedEventArgs args, Boolean reRaised) su System. Windows.UIElement.RaiseEventImpl (mittente DependencyObject, argomenti RoutedEventArgs) su System.Windows.UIElement.RaiseEvent (argomenti RoutedEventArgs, Boolean trusted) su System.Windows.Input.RoutedCommand.ExecuteImpl (parametro Object, destinazione IInputElement, utente BooleanInitiated) su System. Windows.Input.RoutedCommand.ExecuteCore (parametro Object, IInputElement target, Boolean userInitiated) su MS.Internal.Commands.CommandHel pers.CriticalExecuteCommandSource (ICommandSource commandSource, Boolean userInitiated) su System.Windows.Controls.Primitives.ButtonBase.OnClick () su System.Windows.Controls.Button.OnClick () su System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp (MouseButtonEventArgs e) su System.Windows.UIElement.OnMouseLeftButtonUpThunk (mittente dell'oggetto, MouseButtonEventArgs e) su System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler (Delegate genericHandler, Object genericTarget) su System.Windows.RoutedEventArgs.InvokeHandler (Delegate handler, Object target) a System.Windows.RoutedEventHandlerInfo.InvokeHandler (Object target, RoutedEventArgs routedEventArgs) su System.Windows.EventRoute.InvokeHandlersImpl (origine oggetto, RoutedEventArgs args, Boolean reRaised) su System.Windows.UIElement.ReRaiseEventAs (mittente DependencyObject, argomenti RoutedEventArgs, RoutedEvent newEvent) su System.Windows.UIElement.OnMouseUpThunk (mittente dell'oggetto, MouseButtonEventArgs e) su System.Windows.Input.MouseButt onEventArgs.InvokeEventHandler (Delegate genericHandler, Object genericTarget) su System.Windows.RoutedEventArgs.InvokeHandler (Delegate handler, Object target) su System.Windows.RoutedEventHandlerInfo.InvokeHandler (Object target, RoutedEventArgs routedEventArgs) su System.Windows.EventRoute.InvokeHandlersImpl (Object source, RoutedEventArgs args, Boolean reRaised) su System.Windows.UIElement.RaiseEventImpl (Sender DependencyObject, RoutedEventArgs args) su System.Windows.UIElement.RaiseTrustedEvent (RoutedEventArgs args) su System.Windows.UIElement.RaiseEvent (RoutedEventArgs args, Boolean trusted) a System.Windows.Input.InputManager.ProcessStagingArea () a System.Windows.Input.InputManager.ProcessInput (input InputEventArgs) a System.Windows.Input.InputProviderSite.ReportInput (InputReport inputReport) a System.Windows.Interop.HwndMouseInputProvider.ReportInput (IntPtr hwnd, modalità InputMode, Int32 timestamp, azioni RawMouseActions, Int32 x, Int32 y, Int32 wheel) su System.Windows.Interop.H wndMouseInputProvider.FilterMessage (Intptr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean & handled) su System.Windows.Interop.HwndSource.InputFilterMessage (IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean & handled) su MS.Win32. HwndWrapper.WndProc (IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean & handled) su MS.Win32.HwndSubclass.DispatcherCallbackOperation (Object o) su System.Windows.Threading.ExceptionWrapper.InternalRealCall (Delega callback, Object args, Int32 numArgs ) su System.Windows.Threading.ExceptionWrapper.TryCatchWhen (sorgente dell'oggetto, callback delegato, argomenti oggetto, numerointArgs, delegato catchHandler) su System.Windows.Threading.Dispatcher.LegacyInvokeImpl (priorità DispatcherPriority, timeout TimeSpan, metodo Delegate, argomenti oggetto, Int32 numArgs) su MS.Win32.HwndSubclass.SubclassWndProc (IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) su MS.Win32.UnsafeNativeMethods.DispatchMessage (MSG e msg) su System.Windows.Threading.Dispatch er.PushFrameImpl (frame DispatcherFrame) in System.Windows.Threading.Dispatcher.PushFrame (frame DispatcherFrame) in System.Windows.Application.RunDispatcher (Object ignore) in System.Windows.Application.RunInternal (finestra Window) in System.Windows. Application.Run (Finestra finestra) in System.Windows.Application.Run () in Fruits.App.Main () in System.AppDomain._nExecuteAssembly (assembly RuntimeAssembly, String [] args) in System.AppDomain.ExecuteAssembly (String assemblyFile, Evidence assemblySecurity, String [] args) in Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly () in System.Threading.ThreadHelper.ThreadStart_Context (stato dell'oggetto) in System.Threading.ExecutionContext.RunInternal (ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) su System.Threading.ExecutionContext.Run (ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) su System.Threading.ExecutionContext.Run (ExecutionContext exec utionContext, callback ContextCallback, stato dell'oggetto) in System.Threading.ThreadHelper.ThreadStart () InnerException:
Questa è la classe aggiornata 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
}
}
Il MainWindows aggiornato xaml (per aggiungere un pulsante Salva)
<Window x:Class="Fruits.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Fruits"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<RoutedCommand x:Key="AddFruit" />
<RoutedCommand x:Key='SaveFruit' />
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command='{StaticResource AddFruit}'
Executed='AddFruitCommandBinding_Executed'
CanExecute='AddFruitCommandBinding_CanExecute' />
<CommandBinding Command='{StaticResource SaveFruit}'
Executed='SaveFruitCommandBinding_Executed'
CanExecute='SaveFruitCommandBinding_CanExecute' />
</Window.CommandBindings>
<Grid>
<StackPanel Orientation='Vertical'
Margin='10'>
<CheckBox IsChecked="{Binding ShowSelectedFruitOnly}">Selected Fruit Only</CheckBox>
<ListBox x:Name='MyList'
ItemsSource="{Binding FruitsView}"
ItemTemplate='{StaticResource FruitTemp}' />
<StackPanel Orientation="Horizontal"
Margin="0,10,0,0">
<Label Width="100">New Name:</Label>
<TextBox Width="200"
Text="{Binding NewFruitName, Mode=TwoWay }"
/>
</StackPanel>
<StackPanel Orientation="Horizontal"
Margin="0,10,0,0">
<Label Width="100">New Color:</Label>
<!--<TextBox Width="200"
Text="{Binding NewFruitColor, UpdateSourceTrigger=PropertyChanged}" />-->
<TextBox Width="200"
Text="{Binding NewFruitColor, Mode=TwoWay }" />
<ContentControl Style="{StaticResource ColorSwatch}"
Margin="2"
VerticalAlignment="Center"
Content="{Binding NewFruitColor}" />
</StackPanel>
<StackPanel Orientation='Horizontal'>
<Button x:Name='AddFruit'
Height='auto'
Width='auto'
Content='Add New Fruit 2'
Margin='0,10,0,0'
Command='{StaticResource AddFruit}' />
<Button x:Name='SaveFruit'
Height='auto'
Width='auto'
Content='Save Fruit'
Margin='100,10,0,0'
Command='{StaticResource SaveFruit}' />
</StackPanel>
</StackPanel>
</Grid>
</Window>
e il mio codice dietro le mainwindows (gestore aggiunto)
using Fruits.ViewModels;
using System;
using System.Windows;
using System.Windows.Input;
namespace Fruits
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
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;
}
}
}
Il mio dbContext appena aggiunto:
namespace Fruits.ViewModels
{
public class FruitDbContext:DbContext
{
public DbSet<Fruit> Fruits { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
{
optionBuilder.UseSqlServer(@"Server = xxx; Database=Test; Integrated Security = True");
}
}
}
Altre classi rimangono invariate, ma le ho elencate comunque:
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<Fruit>();
}
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<Fruit> _fruits;
public ObservableCollection<Fruit> 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
<Application x:Class="Fruits.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Fruits"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style x:Key="ColorSwatch"
TargetType="ContentControl">
<Setter Property="Width"
Value="24" />
<Setter Property="Height"
Value="24" />
<Setter Property="IsTabStop"
Value="false" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Rectangle HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Stroke="Gray"
StrokeThickness="1">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding}" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key='FruitTemp'>
<StackPanel Orientation='Horizontal'
Margin='5'>
<TextBlock x:Name='tbName'
Text='{Binding FruitName}'
Margin='10,0,0,0'
Width='100' />
<TextBlock x:Name='tbColor'
Text='{Binding FruitColor}'
Margin='10,0,0,0'
Width='100' />
<ContentControl Width="16"
Height="16"
Style="{StaticResource ColorSwatch}"
Content="{Binding FruitColor}" />
<!-- The problem here was you were trying to bind Checked, an event,
instead if IsChecked, a bool? property.
-->
<CheckBox x:Name='cbSelected'
Content='Selected'
Margin='10,0,0,0'
IsChecked='{Binding IsSelected}' />
</StackPanel>
</DataTemplate>
</Application.Resources>
</Application>
La struttura del mio progetto
La mia tabella 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]
Si prega di avvisare perché il messaggio diceva che non esisteva una chiave primaria mentre esiste
Questo messaggio di eccezione non significa che sia necessario definire una chiave primaria nel database , significa che richiede una chiave primaria da definire nella classe .
Sebbene tu abbia provato a farlo:
private Guid _id; [Key] public Guid ID { get { return _id; } }
Ciò non ha alcun effetto, poiché Entity Framework ignora le proprietà di sola lettura. Deve: quando recupera un record Fruits
dal database, costruisce un oggetto Fruit
e quindi chiama i setter della proprietà per ogni proprietà mappata. Non funzionerà mai per le proprietà di sola lettura.
È necessario che Entity Framework sia in grado di impostare il valore ID
. Ciò significa che la proprietà deve avere un setter.
Sono venuto qui con un errore simile:
System.InvalidOperationException: 'Il tipo di entità' MyType 'richiede una chiave primaria da definire.'
Dopo aver letto la risposta di hvd, mi sono reso conto di aver semplicemente dimenticato di rendere "pubblica" la mia proprietà chiave. Questo..
namespace MyApp.Models.Schedule
{
public class MyType
{
[Key]
int Id { get; set; }
// ...
Dovrebbe essere questo ..
namespace MyApp.Models.Schedule
{
public class MyType
{
[Key]
public int Id { get; set; } // must be public!
// ...