PostSharp.Samples.Xaml + EntityFramework(PostSharp.Samples.Xaml + EntityFramework)

在将PostSharp.Samples.Xaml示例转换为使用EntityFramework时,我遇到PostSharp中提到的“k__BackingField”问题, 将k__Backing字段插入到实体类中,导致数据库生成失败 。 您能告诉我如何使用建议的解决方案“使用MulticastAttributeUsageAttribute.AttributeTargets将其限制为属性”。 不会丢失PostSharp的INotifyPropertyChanged? 我试图通过展示EF和PS来说服利益相关者。

这是对AddressModel.cs的更改

using System; using System.ComponentModel; using System.Text; using PostSharp.Patterns.Contracts; using PostSharp.Patterns.Model; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace PostSharp.Samples.Xaml { public class AddressModel : ModelBase { public int AddressId { get; set; } [DisplayName("Address Line 1")] [Patterns.Contracts.Required] public string Line1 { get; set; } [DisplayName("Address Line 2")] public string Line2 { get; set; } [Patterns.Contracts.Required] public string Town { get; set; } public string Country { get; set; } public DateTime Expiration { get; set; } [IgnoreAutoChangeNotification] public TimeSpan Lifetime => DateTime.Now - Expiration; [SafeForDependencyAnalysis] public string FullAddress { get { var stringBuilder = new StringBuilder(); if (Line1 != null) stringBuilder.Append(Line1); if (Line2 != null) { if (stringBuilder.Length > 0) stringBuilder.Append("; "); stringBuilder.Append(Line2); } if (Town != null) { if (stringBuilder.Length > 0) stringBuilder.Append("; "); stringBuilder.Append(Town); } if (Country != null) { if (stringBuilder.Length > 0) stringBuilder.Append("; "); stringBuilder.Append(Country); } return stringBuilder.ToString(); } } [ForeignKey("AddressId")] [Parent] public virtual CustomerModel Person { get; set; } } }

的app.config

<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="PostSharp" publicKeyToken="b13fd38b8f9c99d7" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.0.37.0" newVersion="5.0.37.0" /> </dependentAssembly> </assemblyBinding> </runtime> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>

CustomerModel.cs

using System.IO; using System.Threading; using PostSharp.Patterns.Collections; using PostSharp.Patterns.Contracts; using PostSharp.Patterns.Model; using PostSharp.Patterns.Threading; using System; using System.ComponentModel.DataAnnotations; using PostSharp.Extensibility; namespace PostSharp.Samples.Xaml { public class CustomerModel : ModelBase { [Key] public int CustomerId { get; set; } public string FirstName { get; set; } [Patterns.Contracts.Required] public string LastName { get; set; } public string Phone { get; set; } public string Mobile { get; set; } public string Email { get; set; } [Child] public AdvisableCollection<AddressModel> Addresses { get; set; } [Reference] public AddressModel PrincipalAddress { get; set; } [Reader] public void Save(string path) { using (var stringWriter = new StreamWriter(path)) { // We need to make sure the object graph is not being modified when we save, // and this is ensured by [ReaderWriterSynchronized] in ModelBase. stringWriter.WriteLine($"UserID: {CustomerId}"); //Thread.Sleep(1000); stringWriter.WriteLine($"FirstName: {FirstName}"); //Thread.Sleep(1000); stringWriter.WriteLine($"LastName: {LastName}"); //Thread.Sleep(1000); stringWriter.WriteLine($"Phone: {Phone}"); //Thread.Sleep(1000); stringWriter.WriteLine($"Mobile: {Mobile}"); //Thread.Sleep(1000); stringWriter.WriteLine($"Email: {Email}"); //Thread.Sleep(1000); foreach (var address in Addresses) { //Thread.Sleep(1000); if (address == PrincipalAddress) stringWriter.WriteLine($"Principal address: {address}"); else stringWriter.WriteLine($"Secondary address: {address}"); } try { using (var ctx = new EFContext()) { ctx.Person.Add(this); ctx.SaveChanges(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } } } }

MainWindow.xaml

<Window x:Class="PostSharp.Samples.Xaml.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:xaml="clr-namespace:PostSharp.Samples.Xaml" xmlns:controls="clr-namespace:PostSharp.Patterns.Model.Controls;assembly=PostSharp.Patterns.Xaml" Title="Contact Form" Height="352.584" Width="352.753" x:Name="Window"> <Window.DataContext> <xaml:CustomerViewModel /> </Window.DataContext> <DockPanel> <ToolBarTray DockPanel.Dock="Top"> <ToolBar> <Button Name="SaveButton" Command="{Binding ElementName=Window, Path=SaveCommand}">Save</Button> <controls:UndoButton /> <controls:RedoButton /> </ToolBar> </ToolBarTray> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="29*" /> <ColumnDefinition Width="316*" /> </Grid.ColumnDefinitions> <Label Content="First Name:" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,62,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.FirstName, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="Last Name:" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,92,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.LastName, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="Principal Address:" HorizontalAlignment="Left" Margin="10,119,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.105,-0.462" Grid.ColumnSpan="2" /> <ComboBox HorizontalAlignment="Left" Margin="97.846,120,0,0" VerticalAlignment="Top" Width="189" ItemsSource="{Binding Path=Customer.Addresses}" SelectedValue="{Binding Path=Customer.PrincipalAddress}" DisplayMemberPath="FullAddress" Grid.Column="1" /> <xaml:FancyTextBlock HorizontalAlignment="Left" Margin="10,15,0,0" Text="{Binding Path=FullName}" VerticalAlignment="Top" Width="214" Grid.ColumnSpan="2" /> <Label Content="Line1:" HorizontalAlignment="Left" Margin="11.846,150,0,0" VerticalAlignment="Top" Grid.Column="1" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,154,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.PrincipalAddress.Line1, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="Town:" HorizontalAlignment="Left" Margin="12.846,176,0,0" VerticalAlignment="Top" Grid.Column="1" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,182,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.PrincipalAddress.Town, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="User Id:" HorizontalAlignment="Left" Margin="11.846,210,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,210,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.UserID, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> </Grid> </DockPanel> </Window>

MainWindow.xaml.cs

using System.Windows; using System.Windows.Input; using Microsoft.Win32; using PostSharp.Patterns.Collections; using PostSharp.Patterns.Model; using PostSharp.Patterns.Recording; using PostSharp.Patterns.Threading; using PostSharp.Patterns.Xaml; namespace PostSharp.Samples.Xaml { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> [NotifyPropertyChanged] public partial class MainWindow : Window { /* private readonly */ public readonly CustomerModel customer = new CustomerModel { Email = "2", Mobile = "3", Phone = "4", CustomerId = 1, FirstName = "Jan", LastName = "Novak", Addresses = new AdvisableCollection<AddressModel> { new AddressModel { Line1 = "Saldova 1G", Town = "Prague", Country = "USA" }, new AddressModel { Line1 = "Tyrsova 25", Town = "Brno", Country = "USA" }, new AddressModel { Line1 = "Pivorarka 154", Town = "Pilsen", Country = "USA" } } }; private readonly Recorder recorder; public MainWindow() { // We need to have a local reference for [NotifyPropertyChanged] to work. recorder = RecordingServices.DefaultRecorder; InitializeComponent(); // Register our custom operation formatter. RecordingServices.OperationFormatter = new MyOperationFormatter(RecordingServices.OperationFormatter); // Create initial data. var customerViewModel = new CustomerViewModel {Customer = customer}; customerViewModel.Customer.PrincipalAddress = customerViewModel.Customer.Addresses[0]; // Clear the initialization steps from the recorder. recorder.Clear(); DataContext = customerViewModel; } [Command] public ICommand SaveCommand { get; private set; } public bool CanExecuteSave => recorder.UndoOperations.Count > 0; private void ExecuteSave() { var openFileDialog = new SaveFileDialog(); if (openFileDialog.ShowDialog().GetValueOrDefault()) Save(openFileDialog.FileName); } [Background] [DisableUI] private void Save(string path) { customer.Save(path); } } }

packages.config

<?xml version="1.0" encoding="utf-8"?> <packages> <package id="EntityFramework" version="6.2.0" targetFramework="net461" /> <package id="PostSharp" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Aggregation" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Aggregation.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Common" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Common.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Model" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Model.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Threading" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Threading.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Xaml" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Xaml.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Redist" version="5.0.40" targetFramework="net461" /> </packages>

PostSharp.Samples.Xaml.csproj

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\..\packages\PostSharp.5.0.40\build\PostSharp.props" Condition="Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{B10B8456-75F7-4D68-9775-CC87517B07B6}</ProjectGuid> <OutputType>WinExe</OutputType> <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>PostSharp.Samples.Xaml</RootNamespace> <AssemblyName>PostSharp.Samples.Xaml</AssemblyName> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <WarningLevel>4</WarningLevel> <NuGetPackageImportStamp> </NuGetPackageImportStamp> <LangVersion>6</LangVersion> <TargetFrameworkProfile /> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Prefer32Bit>false</Prefer32Bit> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Prefer32Bit>false</Prefer32Bit> </PropertyGroup> <ItemGroup> <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> <HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> <HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp, Version=5.0.40.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7"> <HintPath>..\..\packages\PostSharp.Redist.5.0.40\lib\net35-client\PostSharp.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Aggregation"> <HintPath>..\..\packages\PostSharp.Patterns.Aggregation.Redist.5.0.40\lib\net45\PostSharp.Patterns.Aggregation.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Common"> <HintPath>..\..\packages\PostSharp.Patterns.Common.Redist.5.0.40\lib\net46\PostSharp.Patterns.Common.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Model"> <HintPath>..\..\packages\PostSharp.Patterns.Model.Redist.5.0.40\lib\net40\PostSharp.Patterns.Model.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Threading"> <HintPath>..\..\packages\PostSharp.Patterns.Threading.Redist.5.0.40\lib\net45\PostSharp.Patterns.Threading.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Xaml"> <HintPath>..\..\packages\PostSharp.Patterns.Xaml.Redist.5.0.40\lib\net40\PostSharp.Patterns.Xaml.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="System" /> <Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.Data" /> <Reference Include="System.Xml" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Xaml"> <RequiredTargetFramework>4.0</RequiredTargetFramework> </Reference> <Reference Include="WindowsBase" /> <Reference Include="PresentationCore" /> <Reference Include="PresentationFramework" /> </ItemGroup> <ItemGroup> <ApplicationDefinition Include="App.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> <Page Include="FancyTextBlock.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> <Compile Include="AddressModel.cs" /> <Compile Include="App.xaml.cs"> <DependentUpon>App.xaml</DependentUpon> <SubType>Code</SubType> </Compile> <Compile Include="CustomerModel.cs" /> <Compile Include="CustomerViewModel.cs" /> <Compile Include="DisableUIAttribute.cs" /> <Compile Include="EFContext.cs" /> <Compile Include="FancyTextBlock.xaml.cs"> <DependentUpon>FancyTextBlock.xaml</DependentUpon> </Compile> <Compile Include="MainWindow.xaml.cs"> <DependentUpon>MainWindow.xaml</DependentUpon> <SubType>Code</SubType> </Compile> </ItemGroup> <ItemGroup> <Compile Include="ModelBase.cs" /> <Compile Include="MyOperationFormatter.cs" /> <Compile Include="Properties\AssemblyInfo.cs"> <SubType>Code</SubType> </Compile> <Compile Include="Properties\Resources.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> <DependentUpon>Resources.resx</DependentUpon> </Compile> <Compile Include="Properties\Settings.Designer.cs"> <AutoGen>True</AutoGen> <DependentUpon>Settings.settings</DependentUpon> <DesignTimeSharedInput>True</DesignTimeSharedInput> </Compile> <EmbeddedResource Include="Properties\Resources.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>Resources.Designer.cs</LastGenOutput> </EmbeddedResource> <None Include="app.config" /> <None Include="packages.config" /> <None Include="Properties\Settings.settings"> <Generator>SettingsSingleFileGenerator</Generator> <LastGenOutput>Settings.Designer.cs</LastGenOutput> </None> <AppDesigner Include="Properties\" /> <None Include="README.md" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <PropertyGroup> <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText> </PropertyGroup> <Error Condition="!Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.5.0.40\build\PostSharp.props'))" /> <Error Condition="!Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.5.0.40\build\PostSharp.targets'))" /> </Target> <Import Project="..\..\packages\PostSharp.5.0.40\build\PostSharp.targets" Condition="Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.targets')" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> </Target> <Target Name="AfterBuild"> </Target> -->

其他一切都保持不变,当您更改应用中的任何字段并尝试保存时,您应该可以构建它并在CustomerModel.cs行#80中查看异常。

你得到以下ex.Message;

One or more validation errors were detected during model generation: PostSharp.Samples.Xaml.AddressModel: : EntityType 'AddressModel' has no key defined. Define the key for this EntityType. <Person>k__BackingField: Name: The specified name is not allowed: '<Person>k__BackingField'. PostSharp.Samples.Xaml.AddressModel_<Person>k__BackingField: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField'. AddressModel_<Person>k__BackingField_Source: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField_Source'. AddressModel_<Person>k__BackingField_Target: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField_Target'. AddressModels: EntityType: EntitySet 'AddressModels' is based on type 'AddressModel' that has no keys defined. AddressModel_<Person>k__BackingField: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField'.

原始示例解决方案EFContext.cs中没有的新文件:

using System.Data.Entity; namespace PostSharp.Samples.Xaml { public class EFContext :DbContext { public DbSet<CustomerModel> Person { get; set; } } }

While converting PostSharp.Samples.Xaml sample to use EntityFramework, I am encountering "k__BackingField" problem mentioned in PostSharp inserting k__Backing Field into Entity Class, causing Database generation to fail. Can you show me how to use the proposed solution "Use MulticastAttributeUsageAttribute.AttributeTargets to restrict it to properties." without losing PostSharp's INotifyPropertyChanged? I am trying to persuade the stakeholders by showcasing both EF and PS.

Here's the change to AddressModel.cs

using System; using System.ComponentModel; using System.Text; using PostSharp.Patterns.Contracts; using PostSharp.Patterns.Model; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace PostSharp.Samples.Xaml { public class AddressModel : ModelBase { public int AddressId { get; set; } [DisplayName("Address Line 1")] [Patterns.Contracts.Required] public string Line1 { get; set; } [DisplayName("Address Line 2")] public string Line2 { get; set; } [Patterns.Contracts.Required] public string Town { get; set; } public string Country { get; set; } public DateTime Expiration { get; set; } [IgnoreAutoChangeNotification] public TimeSpan Lifetime => DateTime.Now - Expiration; [SafeForDependencyAnalysis] public string FullAddress { get { var stringBuilder = new StringBuilder(); if (Line1 != null) stringBuilder.Append(Line1); if (Line2 != null) { if (stringBuilder.Length > 0) stringBuilder.Append("; "); stringBuilder.Append(Line2); } if (Town != null) { if (stringBuilder.Length > 0) stringBuilder.Append("; "); stringBuilder.Append(Town); } if (Country != null) { if (stringBuilder.Length > 0) stringBuilder.Append("; "); stringBuilder.Append(Country); } return stringBuilder.ToString(); } } [ForeignKey("AddressId")] [Parent] public virtual CustomerModel Person { get; set; } } }

app.config

<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="PostSharp" publicKeyToken="b13fd38b8f9c99d7" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.0.37.0" newVersion="5.0.37.0" /> </dependentAssembly> </assemblyBinding> </runtime> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>

CustomerModel.cs

using System.IO; using System.Threading; using PostSharp.Patterns.Collections; using PostSharp.Patterns.Contracts; using PostSharp.Patterns.Model; using PostSharp.Patterns.Threading; using System; using System.ComponentModel.DataAnnotations; using PostSharp.Extensibility; namespace PostSharp.Samples.Xaml { public class CustomerModel : ModelBase { [Key] public int CustomerId { get; set; } public string FirstName { get; set; } [Patterns.Contracts.Required] public string LastName { get; set; } public string Phone { get; set; } public string Mobile { get; set; } public string Email { get; set; } [Child] public AdvisableCollection<AddressModel> Addresses { get; set; } [Reference] public AddressModel PrincipalAddress { get; set; } [Reader] public void Save(string path) { using (var stringWriter = new StreamWriter(path)) { // We need to make sure the object graph is not being modified when we save, // and this is ensured by [ReaderWriterSynchronized] in ModelBase. stringWriter.WriteLine($"UserID: {CustomerId}"); //Thread.Sleep(1000); stringWriter.WriteLine($"FirstName: {FirstName}"); //Thread.Sleep(1000); stringWriter.WriteLine($"LastName: {LastName}"); //Thread.Sleep(1000); stringWriter.WriteLine($"Phone: {Phone}"); //Thread.Sleep(1000); stringWriter.WriteLine($"Mobile: {Mobile}"); //Thread.Sleep(1000); stringWriter.WriteLine($"Email: {Email}"); //Thread.Sleep(1000); foreach (var address in Addresses) { //Thread.Sleep(1000); if (address == PrincipalAddress) stringWriter.WriteLine($"Principal address: {address}"); else stringWriter.WriteLine($"Secondary address: {address}"); } try { using (var ctx = new EFContext()) { ctx.Person.Add(this); ctx.SaveChanges(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } } } }

MainWindow.xaml

<Window x:Class="PostSharp.Samples.Xaml.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:xaml="clr-namespace:PostSharp.Samples.Xaml" xmlns:controls="clr-namespace:PostSharp.Patterns.Model.Controls;assembly=PostSharp.Patterns.Xaml" Title="Contact Form" Height="352.584" Width="352.753" x:Name="Window"> <Window.DataContext> <xaml:CustomerViewModel /> </Window.DataContext> <DockPanel> <ToolBarTray DockPanel.Dock="Top"> <ToolBar> <Button Name="SaveButton" Command="{Binding ElementName=Window, Path=SaveCommand}">Save</Button> <controls:UndoButton /> <controls:RedoButton /> </ToolBar> </ToolBarTray> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="29*" /> <ColumnDefinition Width="316*" /> </Grid.ColumnDefinitions> <Label Content="First Name:" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,62,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.FirstName, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="Last Name:" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,92,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.LastName, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="Principal Address:" HorizontalAlignment="Left" Margin="10,119,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.105,-0.462" Grid.ColumnSpan="2" /> <ComboBox HorizontalAlignment="Left" Margin="97.846,120,0,0" VerticalAlignment="Top" Width="189" ItemsSource="{Binding Path=Customer.Addresses}" SelectedValue="{Binding Path=Customer.PrincipalAddress}" DisplayMemberPath="FullAddress" Grid.Column="1" /> <xaml:FancyTextBlock HorizontalAlignment="Left" Margin="10,15,0,0" Text="{Binding Path=FullName}" VerticalAlignment="Top" Width="214" Grid.ColumnSpan="2" /> <Label Content="Line1:" HorizontalAlignment="Left" Margin="11.846,150,0,0" VerticalAlignment="Top" Grid.Column="1" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,154,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.PrincipalAddress.Line1, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="Town:" HorizontalAlignment="Left" Margin="12.846,176,0,0" VerticalAlignment="Top" Grid.Column="1" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,182,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.PrincipalAddress.Town, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> <Label Content="User Id:" HorizontalAlignment="Left" Margin="11.846,210,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2" /> <TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,210,0,0" TextWrapping="Wrap" Text="{Binding Path=Customer.UserID, ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="189" Grid.Column="1" /> </Grid> </DockPanel> </Window>

MainWindow.xaml.cs

using System.Windows; using System.Windows.Input; using Microsoft.Win32; using PostSharp.Patterns.Collections; using PostSharp.Patterns.Model; using PostSharp.Patterns.Recording; using PostSharp.Patterns.Threading; using PostSharp.Patterns.Xaml; namespace PostSharp.Samples.Xaml { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> [NotifyPropertyChanged] public partial class MainWindow : Window { /* private readonly */ public readonly CustomerModel customer = new CustomerModel { Email = "2", Mobile = "3", Phone = "4", CustomerId = 1, FirstName = "Jan", LastName = "Novak", Addresses = new AdvisableCollection<AddressModel> { new AddressModel { Line1 = "Saldova 1G", Town = "Prague", Country = "USA" }, new AddressModel { Line1 = "Tyrsova 25", Town = "Brno", Country = "USA" }, new AddressModel { Line1 = "Pivorarka 154", Town = "Pilsen", Country = "USA" } } }; private readonly Recorder recorder; public MainWindow() { // We need to have a local reference for [NotifyPropertyChanged] to work. recorder = RecordingServices.DefaultRecorder; InitializeComponent(); // Register our custom operation formatter. RecordingServices.OperationFormatter = new MyOperationFormatter(RecordingServices.OperationFormatter); // Create initial data. var customerViewModel = new CustomerViewModel {Customer = customer}; customerViewModel.Customer.PrincipalAddress = customerViewModel.Customer.Addresses[0]; // Clear the initialization steps from the recorder. recorder.Clear(); DataContext = customerViewModel; } [Command] public ICommand SaveCommand { get; private set; } public bool CanExecuteSave => recorder.UndoOperations.Count > 0; private void ExecuteSave() { var openFileDialog = new SaveFileDialog(); if (openFileDialog.ShowDialog().GetValueOrDefault()) Save(openFileDialog.FileName); } [Background] [DisableUI] private void Save(string path) { customer.Save(path); } } }

packages.config

<?xml version="1.0" encoding="utf-8"?> <packages> <package id="EntityFramework" version="6.2.0" targetFramework="net461" /> <package id="PostSharp" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Aggregation" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Aggregation.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Common" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Common.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Model" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Model.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Threading" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Threading.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Patterns.Xaml" version="5.0.40" targetFramework="net461" developmentDependency="true" /> <package id="PostSharp.Patterns.Xaml.Redist" version="5.0.40" targetFramework="net461" /> <package id="PostSharp.Redist" version="5.0.40" targetFramework="net461" /> </packages>

PostSharp.Samples.Xaml.csproj

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\..\packages\PostSharp.5.0.40\build\PostSharp.props" Condition="Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{B10B8456-75F7-4D68-9775-CC87517B07B6}</ProjectGuid> <OutputType>WinExe</OutputType> <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>PostSharp.Samples.Xaml</RootNamespace> <AssemblyName>PostSharp.Samples.Xaml</AssemblyName> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <WarningLevel>4</WarningLevel> <NuGetPackageImportStamp> </NuGetPackageImportStamp> <LangVersion>6</LangVersion> <TargetFrameworkProfile /> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Prefer32Bit>false</Prefer32Bit> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <Prefer32Bit>false</Prefer32Bit> </PropertyGroup> <ItemGroup> <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> <HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> <HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp, Version=5.0.40.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7"> <HintPath>..\..\packages\PostSharp.Redist.5.0.40\lib\net35-client\PostSharp.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Aggregation"> <HintPath>..\..\packages\PostSharp.Patterns.Aggregation.Redist.5.0.40\lib\net45\PostSharp.Patterns.Aggregation.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Common"> <HintPath>..\..\packages\PostSharp.Patterns.Common.Redist.5.0.40\lib\net46\PostSharp.Patterns.Common.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Model"> <HintPath>..\..\packages\PostSharp.Patterns.Model.Redist.5.0.40\lib\net40\PostSharp.Patterns.Model.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Threading"> <HintPath>..\..\packages\PostSharp.Patterns.Threading.Redist.5.0.40\lib\net45\PostSharp.Patterns.Threading.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="PostSharp.Patterns.Xaml"> <HintPath>..\..\packages\PostSharp.Patterns.Xaml.Redist.5.0.40\lib\net40\PostSharp.Patterns.Xaml.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="System" /> <Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.Data" /> <Reference Include="System.Xml" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Xaml"> <RequiredTargetFramework>4.0</RequiredTargetFramework> </Reference> <Reference Include="WindowsBase" /> <Reference Include="PresentationCore" /> <Reference Include="PresentationFramework" /> </ItemGroup> <ItemGroup> <ApplicationDefinition Include="App.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> <Page Include="FancyTextBlock.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> <Compile Include="AddressModel.cs" /> <Compile Include="App.xaml.cs"> <DependentUpon>App.xaml</DependentUpon> <SubType>Code</SubType> </Compile> <Compile Include="CustomerModel.cs" /> <Compile Include="CustomerViewModel.cs" /> <Compile Include="DisableUIAttribute.cs" /> <Compile Include="EFContext.cs" /> <Compile Include="FancyTextBlock.xaml.cs"> <DependentUpon>FancyTextBlock.xaml</DependentUpon> </Compile> <Compile Include="MainWindow.xaml.cs"> <DependentUpon>MainWindow.xaml</DependentUpon> <SubType>Code</SubType> </Compile> </ItemGroup> <ItemGroup> <Compile Include="ModelBase.cs" /> <Compile Include="MyOperationFormatter.cs" /> <Compile Include="Properties\AssemblyInfo.cs"> <SubType>Code</SubType> </Compile> <Compile Include="Properties\Resources.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> <DependentUpon>Resources.resx</DependentUpon> </Compile> <Compile Include="Properties\Settings.Designer.cs"> <AutoGen>True</AutoGen> <DependentUpon>Settings.settings</DependentUpon> <DesignTimeSharedInput>True</DesignTimeSharedInput> </Compile> <EmbeddedResource Include="Properties\Resources.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>Resources.Designer.cs</LastGenOutput> </EmbeddedResource> <None Include="app.config" /> <None Include="packages.config" /> <None Include="Properties\Settings.settings"> <Generator>SettingsSingleFileGenerator</Generator> <LastGenOutput>Settings.Designer.cs</LastGenOutput> </None> <AppDesigner Include="Properties\" /> <None Include="README.md" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <PropertyGroup> <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText> </PropertyGroup> <Error Condition="!Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.5.0.40\build\PostSharp.props'))" /> <Error Condition="!Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.5.0.40\build\PostSharp.targets'))" /> </Target> <Import Project="..\..\packages\PostSharp.5.0.40\build\PostSharp.targets" Condition="Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.targets')" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> </Target> <Target Name="AfterBuild"> </Target> -->

Everything else remain the same, and you should be able to build it and see the exception in CustomerModel.cs line#80 when you change any field in the app and try to save.

You get following ex.Message;

One or more validation errors were detected during model generation: PostSharp.Samples.Xaml.AddressModel: : EntityType 'AddressModel' has no key defined. Define the key for this EntityType. <Person>k__BackingField: Name: The specified name is not allowed: '<Person>k__BackingField'. PostSharp.Samples.Xaml.AddressModel_<Person>k__BackingField: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField'. AddressModel_<Person>k__BackingField_Source: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField_Source'. AddressModel_<Person>k__BackingField_Target: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField_Target'. AddressModels: EntityType: EntitySet 'AddressModels' is based on type 'AddressModel' that has no keys defined. AddressModel_<Person>k__BackingField: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField'.

A new file that was not in the original sample solution, EFContext.cs:

using System.Data.Entity; namespace PostSharp.Samples.Xaml { public class EFContext :DbContext { public DbSet<CustomerModel> Person { get; set; } } }

最满意答案

您可以在运行时使用OnModelCreating和modelBuilder.Entity<T>.Ignore(x => x.Property)以编程方式忽略失败的属性。 以下演示对EFContext更改:

protected override void OnModelCreating(DbModelBuilder modelBuilder) { IgnorePostSharpProperties<CustomerModel>(modelBuilder); IgnorePostSharpProperties<AddressModel>(modelBuilder); base.OnModelCreating(modelBuilder); } public void IgnorePostSharpProperties<T>(DbModelBuilder modelBuilder) where T : class { foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)) { // We don't want properties that are compiler-generated with a special PostSharp name. if (!property.IsDefined(typeof(CompilerGeneratedAttribute), false) || !property.Name.Contains("<")) continue; ParameterExpression paramExpression = Expression.Parameter(typeof(T)); Expression expression = Expression.Lambda( Expression.Property(paramExpression, property.Name), paramExpression ); EntityTypeConfiguration<T> entityTypeConfig = modelBuilder.Entity<T>(); MethodInfo ignoreMethodDefinition = entityTypeConfig.GetType().GetMethod("Ignore", BindingFlags.Public | BindingFlags.Instance); MethodInfo ignoreMethodInstance = ignoreMethodDefinition.MakeGenericMethod(property.PropertyType); // Ignore the property. ignoreMethodInstance.Invoke(entityTypeConfig, new object[] {expression}); } }

You can use OnModelCreating and modelBuilder.Entity<T>.Ignore(x => x.Property) in runtime to ignore the failing property programmatically. The following demonstrates changes to EFContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder) { IgnorePostSharpProperties<CustomerModel>(modelBuilder); IgnorePostSharpProperties<AddressModel>(modelBuilder); base.OnModelCreating(modelBuilder); } public void IgnorePostSharpProperties<T>(DbModelBuilder modelBuilder) where T : class { foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)) { // We don't want properties that are compiler-generated with a special PostSharp name. if (!property.IsDefined(typeof(CompilerGeneratedAttribute), false) || !property.Name.Contains("<")) continue; ParameterExpression paramExpression = Expression.Parameter(typeof(T)); Expression expression = Expression.Lambda( Expression.Property(paramExpression, property.Name), paramExpression ); EntityTypeConfiguration<T> entityTypeConfig = modelBuilder.Entity<T>(); MethodInfo ignoreMethodDefinition = entityTypeConfig.GetType().GetMethod("Ignore", BindingFlags.Public | BindingFlags.Instance); MethodInfo ignoreMethodInstance = ignoreMethodDefinition.MakeGenericMethod(property.PropertyType); // Ignore the property. ignoreMethodInstance.Invoke(entityTypeConfig, new object[] {expression}); } }

更多推荐