Lazy-loading proxies package
Part of #10509, #3797. This change adds a new package--Microsoft.EntityFrameworkCore.Proxies--that contains a lazy-loading proxy implementation making use of the EF lazy-loading infrastructure and the Castle.Core proxies package. Current plan is for this to ship on NuGet as an optional package for use with EF. To use in a normal application, just add a call to `UseLazyLoadingProxies()` on the DbContext options builder. There are also `context.CreateProxy()` methods for creating stand-alone proxy instances if needed. Entity types must be public and navigation properties must be virtual. Also, the entity type and constructor must be "visible" to the castle proxies assembly, which usually means public, but could mean internal if "InternalsVisibleTo" is used. Exceptions are thrown if these requirements are not met--you don't just not get a proxy like in EF6. Note that this is an optional package for EF that we chose to create because the infrastructure in place made it easy to do so. It does not preclude a Roslyn-based rewriting solution in the future.
This commit is contained in:
Родитель
714e13180f
Коммит
972b71ffaf
14
EFCore.sln
14
EFCore.sln
|
@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Design", "src\EFCore
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.InMemory", "src\EFCore.InMemory\EFCore.InMemory.csproj", "{6B102CC4-4396-4A7B-9F72-2C6B5C4D8310}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Proxies", "src\EFCore.Proxies\EFCore.Proxies.csproj", "{467616E6-30EA-4861-B96B-9ABD46BE41EA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Relational", "src\EFCore.Relational\EFCore.Relational.csproj", "{6A25DF99-2615-46D8-9532-821764647EE1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Sqlite", "src\EFCore.Sqlite\EFCore.Sqlite.csproj", "{6FF0B963-0544-4428-8AE1-5A71872B55AD}"
|
||||
|
@ -67,6 +69,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.InMemory.FunctionalT
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.InMemory.Tests", "test\EFCore.InMemory.Tests\EFCore.InMemory.Tests.csproj", "{AB8D4BD7-8AB6-4004-AF21-32790EED93BC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Proxies.Tests", "test\EFCore.Proxies.Tests\EFCore.Proxies.Tests.csproj", "{007D03D8-D4A2-4398-A39C-F84A90027161}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Relational.Specification.Tests", "src\EFCore.Relational.Specification.Tests\EFCore.Relational.Specification.Tests.csproj", "{07FA2B15-A6A5-4292-A096-7771FB32EEDA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Relational.Tests", "test\EFCore.Relational.Tests\EFCore.Relational.Tests.csproj", "{1A884122-DC9E-42B1-8821-E43340F954D1}"
|
||||
|
@ -119,6 +123,10 @@ Global
|
|||
{6B102CC4-4396-4A7B-9F72-2C6B5C4D8310}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6B102CC4-4396-4A7B-9F72-2C6B5C4D8310}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6B102CC4-4396-4A7B-9F72-2C6B5C4D8310}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{467616E6-30EA-4861-B96B-9ABD46BE41EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{467616E6-30EA-4861-B96B-9ABD46BE41EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{467616E6-30EA-4861-B96B-9ABD46BE41EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{467616E6-30EA-4861-B96B-9ABD46BE41EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6A25DF99-2615-46D8-9532-821764647EE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6A25DF99-2615-46D8-9532-821764647EE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6A25DF99-2615-46D8-9532-821764647EE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -167,6 +175,10 @@ Global
|
|||
{AB8D4BD7-8AB6-4004-AF21-32790EED93BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AB8D4BD7-8AB6-4004-AF21-32790EED93BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AB8D4BD7-8AB6-4004-AF21-32790EED93BC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{007D03D8-D4A2-4398-A39C-F84A90027161}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{007D03D8-D4A2-4398-A39C-F84A90027161}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{007D03D8-D4A2-4398-A39C-F84A90027161}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{007D03D8-D4A2-4398-A39C-F84A90027161}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{07FA2B15-A6A5-4292-A096-7771FB32EEDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{07FA2B15-A6A5-4292-A096-7771FB32EEDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07FA2B15-A6A5-4292-A096-7771FB32EEDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -218,6 +230,7 @@ Global
|
|||
{715C38E9-B2F5-4DB2-8025-0C6492DEBDD4} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{D3D0A8E8-EC2F-4E01-8650-8554E186A66F} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{6B102CC4-4396-4A7B-9F72-2C6B5C4D8310} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{467616E6-30EA-4861-B96B-9ABD46BE41EA} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{6A25DF99-2615-46D8-9532-821764647EE1} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{6FF0B963-0544-4428-8AE1-5A71872B55AD} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
{A257C01B-BB91-44BA-831C-1E04F7800AC8} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
|
||||
|
@ -230,6 +243,7 @@ Global
|
|||
{7583E3F0-8B29-4BEA-A55E-E8B66E6FD508} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{305B30D3-0E30-46E9-BA9D-060E0B79BE98} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{AB8D4BD7-8AB6-4004-AF21-32790EED93BC} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{007D03D8-D4A2-4398-A39C-F84A90027161} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{07FA2B15-A6A5-4292-A096-7771FB32EEDA} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{1A884122-DC9E-42B1-8821-E43340F954D1} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
{1A73D95E-E8B5-4F96-908C-7B040E4F7AFE} = {258D5057-81B9-40EC-A872-D21E27452749}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<SystemDataSqlClientPackageVersion>4.5.0-preview1-26016-05</SystemDataSqlClientPackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview1-26016-05</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemInteractiveAsyncPackageVersion>3.1.1</SystemInteractiveAsyncPackageVersion>
|
||||
<CastleCorePackageVersion>4.2.1</CastleCorePackageVersion>
|
||||
<XunitAssertPackageVersion>2.3.1</XunitAssertPackageVersion>
|
||||
<XunitCorePackageVersion>2.3.1</XunitCorePackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.3.1</XunitRunnerVisualStudioPackageVersion>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public class LazyLoadProxyOracleTest : LazyLoadProxyTestBase<LazyLoadProxyOracleTest.LoadOracleFixture>
|
||||
{
|
||||
public LazyLoadProxyOracleTest(LoadOracleFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
public class LoadOracleFixture : LoadFixtureBase
|
||||
{
|
||||
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
protected override ITestStoreFactory TestStoreFactory => OracleTestStoreFactory.Instance;
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
=> base.AddOptions(builder).ConfigureWarnings(
|
||||
c => c
|
||||
.Log(RelationalEventId.QueryClientEvaluationWarning));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Lazy-loading proxies for EF Core.</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<MinClientVersion>3.6</MinClientVersion>
|
||||
<AssemblyName>Microsoft.EntityFrameworkCore.Proxies</AssemblyName>
|
||||
<RootNamespace>Microsoft.EntityFrameworkCore</RootNamespace>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>$(PackageTags);Lazy-loading</PackageTags>
|
||||
<CodeAnalysisRuleSet>..\EFCore.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Castle.Core" Version="$(CastleCorePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EFCore\EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Properties\ProxiesStrings.Designer.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>ProxiesStrings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\ProxiesStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ProxiesStrings.Designer.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\ProxiesStrings.resx">
|
||||
<CustomToolNamespace>Microsoft.EntityFrameworkCore.Internal</CustomToolNamespace>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="$(StyleCopAnalyzersPackageVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,70 @@
|
|||
// <auto-generated />
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public static class ProxiesStrings
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.EntityFrameworkCore.Properties.ProxiesStrings", typeof(ProxiesStrings).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// UseLazyLoadingProxies requires AddEntityFrameworkProxies to be called on the internal service provider used.
|
||||
/// </summary>
|
||||
public static string ProxyServicesMissing
|
||||
=> GetString("ProxyServicesMissing");
|
||||
|
||||
/// <summary>
|
||||
/// Entity type '{entityType}' is sealed. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.
|
||||
/// </summary>
|
||||
public static string ItsASeal([CanBeNull] object entityType)
|
||||
=> string.Format(
|
||||
GetString("ItsASeal", nameof(entityType)),
|
||||
entityType);
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property '{navigation}' on entity type '{entityType}' is not virtual. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.
|
||||
/// </summary>
|
||||
public static string NonVirtualNavigation([CanBeNull] object navigation, [CanBeNull] object entityType)
|
||||
=> string.Format(
|
||||
GetString("NonVirtualNavigation", nameof(navigation), nameof(entityType)),
|
||||
navigation, entityType);
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property '{navigation}' on entity type '{entityType}' is mapped without a CLR property. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.
|
||||
/// </summary>
|
||||
public static string FieldNavigation([CanBeNull] object navigation, [CanBeNull] object entityType)
|
||||
=> string.Format(
|
||||
GetString("FieldNavigation", nameof(navigation), nameof(entityType)),
|
||||
navigation, entityType);
|
||||
|
||||
/// <summary>
|
||||
/// Unable to create proxy for '{entityType}' because proxies are not enabled. Call 'DbContextOptionsBuilder.UseLazyLoadingProxies' to enable lazy-loading proxies.
|
||||
/// </summary>
|
||||
public static string ProxiesNotEnabled([CanBeNull] object entityType)
|
||||
=> string.Format(
|
||||
GetString("ProxiesNotEnabled", nameof(entityType)),
|
||||
entityType);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<#
|
||||
Session["ResourceFile"] = "ProxiesStrings.resx";
|
||||
#>
|
||||
<#@ include file="..\..\..\tools\Resources.tt" #>
|
|
@ -0,0 +1,135 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ProxyServicesMissing" xml:space="preserve">
|
||||
<value>UseLazyLoadingProxies requires AddEntityFrameworkProxies to be called on the internal service provider used.</value>
|
||||
</data>
|
||||
<data name="ItsASeal" xml:space="preserve">
|
||||
<value>Entity type '{entityType}' is sealed. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.</value>
|
||||
</data>
|
||||
<data name="NonVirtualNavigation" xml:space="preserve">
|
||||
<value>Navigation property '{navigation}' on entity type '{entityType}' is not virtual. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.</value>
|
||||
</data>
|
||||
<data name="FieldNavigation" xml:space="preserve">
|
||||
<value>Navigation property '{navigation}' on entity type '{entityType}' is mapped without a CLR property. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.</value>
|
||||
</data>
|
||||
<data name="ProxiesNotEnabled" xml:space="preserve">
|
||||
<value>Unable to create proxy for '{entityType}' because proxies are not enabled. Call 'DbContextOptionsBuilder.UseLazyLoadingProxies' to enable lazy-loading proxies.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Proxies.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public interface IProxyFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
object CreateLazyLoadingProxy(
|
||||
[NotNull] IEntityType entityType,
|
||||
[NotNull] ILazyLoader loader,
|
||||
[NotNull] object[] constructorArguments);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
object Create(
|
||||
[NotNull] DbContext context,
|
||||
[NotNull] Type entityClrType,
|
||||
[NotNull] params object[] constructorArguments);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Castle.DynamicProxy;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Proxies.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class LazyLoadingInterceptor : IInterceptor
|
||||
{
|
||||
private readonly IEntityType _entityType;
|
||||
private readonly ILazyLoader _loader;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public LazyLoadingInterceptor(
|
||||
[NotNull] IEntityType entityType,
|
||||
[NotNull] ILazyLoader loader)
|
||||
{
|
||||
_entityType = entityType;
|
||||
_loader = loader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual void Intercept(IInvocation invocation)
|
||||
{
|
||||
var methodName = invocation.Method.Name;
|
||||
|
||||
if (methodName.StartsWith("get_", StringComparison.Ordinal))
|
||||
{
|
||||
var navigationName = methodName.Substring(4);
|
||||
var navigation = _entityType.FindNavigation(navigationName);
|
||||
|
||||
if (navigation != null)
|
||||
{
|
||||
_loader.Load(invocation.Proxy, navigationName);
|
||||
}
|
||||
}
|
||||
|
||||
invocation.Proceed();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Proxies.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class ProxiesConventionSetBuilder : IConventionSetBuilder
|
||||
{
|
||||
private readonly IDbContextOptions _options;
|
||||
private readonly IConstructorBindingFactory _constructorBindingFactory;
|
||||
private readonly IProxyFactory _proxyFactory;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public ProxiesConventionSetBuilder(
|
||||
[NotNull] IDbContextOptions options,
|
||||
[NotNull] IConstructorBindingFactory constructorBindingFactory,
|
||||
[NotNull] IProxyFactory proxyFactory)
|
||||
{
|
||||
_options = options;
|
||||
_constructorBindingFactory = constructorBindingFactory;
|
||||
_proxyFactory = proxyFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual ConventionSet AddConventions(ConventionSet conventionSet)
|
||||
{
|
||||
conventionSet.ModelBuiltConventions.Add(
|
||||
new ProxyBindingRewriter(
|
||||
_proxyFactory,
|
||||
_constructorBindingFactory,
|
||||
_options.FindExtension<ProxiesOptionsExtension>()));
|
||||
|
||||
return conventionSet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Proxies.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class ProxiesOptionsExtension : IDbContextOptionsExtension
|
||||
{
|
||||
private bool _useLazyLoadingProxies;
|
||||
private string _logFragment;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public ProxiesOptionsExtension()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected ProxiesOptionsExtension([NotNull] ProxiesOptionsExtension copyFrom)
|
||||
{
|
||||
_useLazyLoadingProxies = copyFrom._useLazyLoadingProxies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
protected virtual ProxiesOptionsExtension Clone() => new ProxiesOptionsExtension(this);
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool UseLazyLoadingProxies => _useLazyLoadingProxies;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual ProxiesOptionsExtension WithLazyLoading(bool useLazyLoadingProxies = true)
|
||||
{
|
||||
var clone = Clone();
|
||||
|
||||
clone._useLazyLoadingProxies = useLazyLoadingProxies;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual long GetServiceProviderHashCode() => _useLazyLoadingProxies ? 541 : 0;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual void Validate(IDbContextOptions options)
|
||||
{
|
||||
if (_useLazyLoadingProxies)
|
||||
{
|
||||
var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
|
||||
if (internalServiceProvider != null)
|
||||
{
|
||||
using (var scope = internalServiceProvider.CreateScope())
|
||||
{
|
||||
if (scope.ServiceProvider
|
||||
.GetService<IEnumerable<IConventionSetBuilder>>()
|
||||
?.Any(s => s is ProxiesConventionSetBuilder) == false)
|
||||
{
|
||||
throw new InvalidOperationException(ProxiesStrings.ProxyServicesMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual bool ApplyServices(IServiceCollection services)
|
||||
{
|
||||
services.AddEntityFrameworkProxies();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual string LogFragment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_logFragment == null)
|
||||
{
|
||||
_logFragment = _useLazyLoadingProxies
|
||||
? "using lazy-loading proxies "
|
||||
: "";
|
||||
}
|
||||
|
||||
return _logFragment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Proxies.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class ProxyBindingRewriter : IModelBuiltConvention
|
||||
{
|
||||
private readonly ConstructorBindingConvention _directBindingConvention;
|
||||
private readonly IProxyFactory _proxyFactory;
|
||||
private readonly ProxiesOptionsExtension _options;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public ProxyBindingRewriter(
|
||||
[NotNull] IProxyFactory proxyFactory,
|
||||
[NotNull] IConstructorBindingFactory bindingFactory,
|
||||
[CanBeNull] ProxiesOptionsExtension options)
|
||||
{
|
||||
_directBindingConvention = new ConstructorBindingConvention(bindingFactory);
|
||||
_proxyFactory = proxyFactory;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder)
|
||||
{
|
||||
if (_options?.UseLazyLoadingProxies == true)
|
||||
{
|
||||
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
|
||||
{
|
||||
if (entityType.ClrType != null
|
||||
&& !entityType.ClrType.IsAbstract)
|
||||
{
|
||||
if (entityType.ClrType.IsSealed)
|
||||
{
|
||||
throw new InvalidOperationException(ProxiesStrings.ItsASeal(entityType.DisplayName()));
|
||||
}
|
||||
|
||||
var binding = (ConstructorBinding)entityType[CoreAnnotationNames.ConstructorBinding];
|
||||
if (binding == null)
|
||||
{
|
||||
_directBindingConvention.Apply(modelBuilder);
|
||||
}
|
||||
|
||||
binding = (ConstructorBinding)entityType[CoreAnnotationNames.ConstructorBinding];
|
||||
|
||||
entityType[CoreAnnotationNames.ConstructorBinding] = RewriteToFactoryBinding(binding);
|
||||
|
||||
foreach (var navigation in entityType.GetNavigations())
|
||||
{
|
||||
if (navigation.PropertyInfo == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
ProxiesStrings.FieldNavigation(navigation.Name, entityType.DisplayName()));
|
||||
}
|
||||
|
||||
if (!navigation.PropertyInfo.GetMethod.IsVirtual)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
ProxiesStrings.NonVirtualNavigation(navigation.Name, entityType.DisplayName()));
|
||||
}
|
||||
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modelBuilder;
|
||||
}
|
||||
|
||||
private FactoryMethodConstructorBinding RewriteToFactoryBinding(ConstructorBinding currentBinding)
|
||||
=> new FactoryMethodConstructorBinding(
|
||||
_proxyFactory,
|
||||
typeof(ProxyFactory).GetTypeInfo().GetDeclaredMethod(nameof(ProxyFactory.CreateLazyLoadingProxy)),
|
||||
new List<ParameterBinding>
|
||||
{
|
||||
new EntityTypeParameterBinding(),
|
||||
new ServiceParameterBinding(typeof(ILazyLoader), typeof(ILazyLoader)),
|
||||
new ObjectArrayParameterBinding(currentBinding.ParameterBindings)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Castle.DynamicProxy;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Proxies.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class ProxyFactory : IProxyFactory
|
||||
{
|
||||
private readonly ProxyGenerator _generator = new ProxyGenerator();
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual object Create(
|
||||
DbContext context,
|
||||
Type entityClrType,
|
||||
params object[] constructorArguments)
|
||||
{
|
||||
var entityType = context.Model.FindRuntimeEntityType(entityClrType);
|
||||
if (entityType == null)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.EntityTypeNotFound(entityClrType.ShortDisplayName()));
|
||||
}
|
||||
|
||||
return CreateLazyLoadingProxy(entityType, context.GetService<ILazyLoader>(), constructorArguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual object CreateLazyLoadingProxy(
|
||||
IEntityType entityType,
|
||||
ILazyLoader loader,
|
||||
object[] constructorArguments)
|
||||
=> _generator.CreateClassProxy(
|
||||
entityType.ClrType,
|
||||
ProxyGenerationOptions.Default,
|
||||
constructorArguments,
|
||||
new LazyLoadingInterceptor(entityType, loader));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Proxies.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods related to use of proxies with Entity Framework Core.
|
||||
/// </summary>
|
||||
public static class ProxiesExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Turns on the creation of lazy-loading proxies.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note that this requires appropriate services to be available in the EF internal service provider. Normally this
|
||||
/// will happen automatically, but if the application is controlling the service provider, then a call to
|
||||
/// <see cref="ProxiesServiceCollectionExtensions.AddEntityFrameworkProxies" /> may be needed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder">
|
||||
/// The options builder, as passed to <see cref="DbContext.OnConfiguring" />
|
||||
/// or exposed AddDbContext.
|
||||
/// </param>
|
||||
/// <param name="useLazyLoadingProxies"> <c>True</c> to use lazy-loading proxies; false to prevent their use. </param>
|
||||
/// <returns> The same builder to allow method calls to be chained. </returns>
|
||||
public static DbContextOptionsBuilder UseLazyLoadingProxies(
|
||||
[NotNull] this DbContextOptionsBuilder optionsBuilder,
|
||||
bool useLazyLoadingProxies = true)
|
||||
{
|
||||
Check.NotNull(optionsBuilder, nameof(optionsBuilder));
|
||||
|
||||
var extension = optionsBuilder.Options.FindExtension<ProxiesOptionsExtension>()
|
||||
?? new ProxiesOptionsExtension();
|
||||
|
||||
extension = extension.WithLazyLoading(useLazyLoadingProxies);
|
||||
|
||||
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Turns on the creation of lazy-loading proxies.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note that this requires appropriate services to be available in the EF internal service provider. Normally this
|
||||
/// will happen automatically, but if the application is controlling the service provider, then a call to
|
||||
/// <see cref="ProxiesServiceCollectionExtensions.AddEntityFrameworkProxies" /> may be needed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext"> The <see cref="DbContext" /> type. </typeparam>
|
||||
/// <param name="optionsBuilder">
|
||||
/// The options builder, as passed to <see cref="DbContext.OnConfiguring" />
|
||||
/// or exposed AddDbContext.
|
||||
/// </param>
|
||||
/// <param name="useLazyLoadingProxies"> <c>True</c> to use lazy-loading proxies; false to prevent their use. </param>
|
||||
/// <returns> The same builder to allow method calls to be chained. </returns>
|
||||
public static DbContextOptionsBuilder<TContext> UseLazyLoadingProxies<TContext>(
|
||||
[NotNull] this DbContextOptionsBuilder<TContext> optionsBuilder,
|
||||
bool useLazyLoadingProxies = true)
|
||||
where TContext : DbContext
|
||||
=> (DbContextOptionsBuilder<TContext>)UseLazyLoadingProxies((DbContextOptionsBuilder)optionsBuilder, useLazyLoadingProxies);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy instance for an entity type if proxy creation has been turned on.
|
||||
/// </summary>
|
||||
/// <param name="context"> The <see cref="DbContext" />. </param>
|
||||
/// <param name="entityType"> The entity type for which a proxy is needed. </param>
|
||||
/// <param name="constructorArguments"> Arguments to pass to the entity type constructor. </param>
|
||||
/// <returns> The proxy instance. </returns>
|
||||
public static object CreateProxy(
|
||||
[NotNull] this DbContext context,
|
||||
[NotNull] Type entityType,
|
||||
[NotNull] params object[] constructorArguments)
|
||||
{
|
||||
Check.NotNull(context, nameof(context));
|
||||
Check.NotNull(entityType, nameof(entityType));
|
||||
Check.NotNull(constructorArguments, nameof(constructorArguments));
|
||||
|
||||
var options = context.GetService<IDbContextOptions>().FindExtension<ProxiesOptionsExtension>();
|
||||
|
||||
if (options?.UseLazyLoadingProxies != true)
|
||||
{
|
||||
throw new InvalidOperationException(ProxiesStrings.ProxiesNotEnabled(entityType.ShortDisplayName()));
|
||||
}
|
||||
|
||||
return context.GetService<IProxyFactory>().Create(context, entityType, constructorArguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy instance for an entity type if proxy creation has been turned on.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"> The entity type for which a proxy is needed. </typeparam>
|
||||
/// <param name="context"> The <see cref="DbContext" />. </param>
|
||||
/// <param name="constructorArguments"> Arguments to pass to the entity type constructor. </param>
|
||||
/// <returns> The proxy instance. </returns>
|
||||
public static TEntity CreateProxy<TEntity>(
|
||||
[NotNull] this DbContext context,
|
||||
[NotNull] params object[] constructorArguments)
|
||||
=> (TEntity)context.CreateProxy(typeof(TEntity), constructorArguments);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Proxies.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Utilities;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// EntityFrameworkCore.Proxies extension methods for <see cref="IServiceCollection" />.
|
||||
/// </summary>
|
||||
public static class ProxiesServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Adds the services required for proxy support in Entity Framework. You use this method when
|
||||
/// using dependency injection in your application, such as with ASP.NET. For more information
|
||||
/// on setting up dependency injection, see http://go.microsoft.com/fwlink/?LinkId=526890.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// You only need to use this functionality when you want Entity Framework to resolve the services it uses
|
||||
/// from an external dependency injection container. If you are not using an external
|
||||
/// dependency injection container, Entity Framework will take care of creating the services it requires.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection"> The <see cref="IServiceCollection" /> to add services to. </param>
|
||||
/// <returns>
|
||||
/// The same service collection so that multiple calls can be chained.
|
||||
/// </returns>
|
||||
public static IServiceCollection AddEntityFrameworkProxies(
|
||||
[NotNull] this IServiceCollection serviceCollection)
|
||||
{
|
||||
Check.NotNull(serviceCollection, nameof(serviceCollection));
|
||||
|
||||
new EntityFrameworkServicesBuilder(serviceCollection)
|
||||
.TryAdd<IConventionSetBuilder, ProxiesConventionSetBuilder>()
|
||||
.TryAddProviderSpecificServices(
|
||||
b => b.TryAddSingleton<IProxyFactory, ProxyFactory>());
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,9 +54,14 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
_model = new LazyRef<IModel>(
|
||||
() =>
|
||||
{
|
||||
var modelBuilder = new ModelBuilder(
|
||||
Dependencies.ConventionSetBuilder.AddConventions(
|
||||
Dependencies.CoreConventionSetBuilder.CreateConventionSet()));
|
||||
var conventionSet = Dependencies.CoreConventionSetBuilder.CreateConventionSet();
|
||||
foreach (var conventionSetBuilder in Dependencies.ConventionSetBuilders)
|
||||
{
|
||||
conventionSet = conventionSetBuilder.AddConventions(conventionSet);
|
||||
}
|
||||
|
||||
var modelBuilder = new ModelBuilder(conventionSet);
|
||||
|
||||
modelBuilder.Entity<HistoryRow>(
|
||||
x =>
|
||||
{
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
|
||||
|
@ -61,7 +64,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
/// <param name="migrationsSqlGenerator"> The SQL generator for Migrations operations. </param>
|
||||
/// <param name="sqlGenerationHelper"> Helpers for generating update SQL. </param>
|
||||
/// <param name="coreConventionSetBuilder"> The core convention set to use when creating the model. </param>
|
||||
/// <param name="conventionSetBuilder"> The convention set to use when creating the model. </param>
|
||||
/// <param name="conventionSetBuilders"> The convention sets to use when creating the model. </param>
|
||||
public HistoryRepositoryDependencies(
|
||||
[NotNull] IRelationalDatabaseCreator databaseCreator,
|
||||
[NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder,
|
||||
|
@ -71,7 +74,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
[NotNull] IMigrationsSqlGenerator migrationsSqlGenerator,
|
||||
[NotNull] ISqlGenerationHelper sqlGenerationHelper,
|
||||
[NotNull] ICoreConventionSetBuilder coreConventionSetBuilder,
|
||||
[NotNull] IConventionSetBuilder conventionSetBuilder)
|
||||
[NotNull] IEnumerable<IConventionSetBuilder> conventionSetBuilders)
|
||||
{
|
||||
Check.NotNull(databaseCreator, nameof(databaseCreator));
|
||||
Check.NotNull(rawSqlCommandBuilder, nameof(rawSqlCommandBuilder));
|
||||
|
@ -81,7 +84,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
Check.NotNull(migrationsSqlGenerator, nameof(migrationsSqlGenerator));
|
||||
Check.NotNull(sqlGenerationHelper, nameof(sqlGenerationHelper));
|
||||
Check.NotNull(coreConventionSetBuilder, nameof(coreConventionSetBuilder));
|
||||
Check.NotNull(conventionSetBuilder, nameof(conventionSetBuilder));
|
||||
Check.NotNull(conventionSetBuilders, nameof(conventionSetBuilders));
|
||||
|
||||
DatabaseCreator = databaseCreator;
|
||||
RawSqlCommandBuilder = rawSqlCommandBuilder;
|
||||
|
@ -91,7 +94,10 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator = migrationsSqlGenerator;
|
||||
SqlGenerationHelper = sqlGenerationHelper;
|
||||
CoreConventionSetBuilder = coreConventionSetBuilder;
|
||||
ConventionSetBuilder = conventionSetBuilder;
|
||||
ConventionSetBuilders = conventionSetBuilders;
|
||||
#pragma warning disable 618
|
||||
ConventionSetBuilder = new CompositeConventionSetBuilder((IReadOnlyList<IConventionSetBuilder>)ConventionSetBuilders);
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -137,8 +143,14 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
/// <summary>
|
||||
/// The convention set to use when creating the model.
|
||||
/// </summary>
|
||||
[Obsolete("Use ConventionSetBuilders")]
|
||||
public IConventionSetBuilder ConventionSetBuilder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The convention set to use when creating the model.
|
||||
/// </summary>
|
||||
public IEnumerable<IConventionSetBuilder> ConventionSetBuilders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
/// </summary>
|
||||
|
@ -154,7 +166,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -171,7 +183,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -188,7 +200,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -205,7 +217,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -222,7 +234,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -239,7 +251,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
migrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -256,7 +268,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
sqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
|
@ -273,13 +285,14 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
coreConventionSetBuilder,
|
||||
ConventionSetBuilder);
|
||||
ConventionSetBuilders);
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
/// </summary>
|
||||
/// <param name="conventionSetBuilder"> The convention set to use when creating the model. </param>
|
||||
/// <returns> A new parameter object with the given service replaced. </returns>
|
||||
[Obsolete("Use With(IEnumerable<IConventionSetBuilder>)")]
|
||||
public HistoryRepositoryDependencies With([NotNull] IConventionSetBuilder conventionSetBuilder)
|
||||
=> new HistoryRepositoryDependencies(
|
||||
DatabaseCreator,
|
||||
|
@ -290,6 +303,23 @@ namespace Microsoft.EntityFrameworkCore.Migrations
|
|||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
conventionSetBuilder);
|
||||
new[] { conventionSetBuilder });
|
||||
|
||||
/// <summary>
|
||||
/// Clones this dependency parameter object with one service replaced.
|
||||
/// </summary>
|
||||
/// <param name="conventionSetBuilders"> The convention sets to use when creating the model. </param>
|
||||
/// <returns> A new parameter object with the given service replaced. </returns>
|
||||
public HistoryRepositoryDependencies With([NotNull] IEnumerable<IConventionSetBuilder> conventionSetBuilders)
|
||||
=> new HistoryRepositoryDependencies(
|
||||
DatabaseCreator,
|
||||
RawSqlCommandBuilder,
|
||||
Connection,
|
||||
Options,
|
||||
ModelDiffer,
|
||||
MigrationsSqlGenerator,
|
||||
SqlGenerationHelper,
|
||||
CoreConventionSetBuilder,
|
||||
conventionSetBuilders);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace Microsoft.EntityFrameworkCore
|
|||
}
|
||||
|
||||
protected virtual IConventionSetBuilder CreateConventionSetBuilder(DbContext context)
|
||||
=> context.GetService<IConventionSetBuilder>();
|
||||
=> new CompositeConventionSetBuilder(context.GetService<IEnumerable<IConventionSetBuilder>>().ToList());
|
||||
|
||||
protected virtual DiagnosticsLogger<DbLoggerCategory.Model> CreateLogger()
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\EFCore\EFCore.csproj" />
|
||||
<ProjectReference Include="..\EFCore.Proxies\EFCore.Proxies.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1217,7 +1217,7 @@ namespace Microsoft.EntityFrameworkCore
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public virtual void Lazy_load_collection_for_detached_throws()
|
||||
public virtual void Lazy_load_collection_for_detached_is_no_op()
|
||||
{
|
||||
using (var context = CreateContext(lazyLoadingEnabled: true))
|
||||
{
|
||||
|
@ -1225,15 +1225,12 @@ namespace Microsoft.EntityFrameworkCore
|
|||
|
||||
context.Entry(parent).State = EntityState.Detached;
|
||||
|
||||
Assert.Equal(
|
||||
CoreStrings.CannotLoadDetached(nameof(Parent.Children), nameof(Parent)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => parent.Children).Message);
|
||||
Assert.Null(parent.Children);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual void Lazy_load_reference_to_principal_for_detached_throws()
|
||||
public virtual void Lazy_load_reference_to_principal_for_detached_is_no_op()
|
||||
{
|
||||
using (var context = CreateContext(lazyLoadingEnabled: true))
|
||||
{
|
||||
|
@ -1241,15 +1238,12 @@ namespace Microsoft.EntityFrameworkCore
|
|||
|
||||
context.Entry(child).State = EntityState.Detached;
|
||||
|
||||
Assert.Equal(
|
||||
CoreStrings.CannotLoadDetached(nameof(Child.Parent), nameof(Child)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => child.Parent).Message);
|
||||
Assert.Null(child.Parent);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual void Lazy_load_reference_to_dependent_for_detached_throws()
|
||||
public virtual void Lazy_load_reference_to_dependent_for_detached_is_no_op()
|
||||
{
|
||||
using (var context = CreateContext(lazyLoadingEnabled: true))
|
||||
{
|
||||
|
@ -1257,10 +1251,7 @@ namespace Microsoft.EntityFrameworkCore
|
|||
|
||||
context.Entry(parent).State = EntityState.Detached;
|
||||
|
||||
Assert.Equal(
|
||||
CoreStrings.CannotLoadDetached(nameof(Parent.Single), nameof(Parent)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => parent.Single).Message);
|
||||
Assert.Null(parent.Single);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -45,7 +45,9 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities
|
|||
|
||||
var serviceProperties = typeof(TDependencies).GetTypeInfo()
|
||||
.DeclaredProperties
|
||||
.Where(p => !ignoreProperties.Contains(p.Name))
|
||||
.Where(
|
||||
p => !ignoreProperties.Contains(p.Name)
|
||||
&& p.CustomAttributes.All(a => a.AttributeType != typeof(ObsoleteAttribute)))
|
||||
.ToList();
|
||||
|
||||
Assert.Equal(constructorParameters.Length, serviceProperties.Count);
|
||||
|
@ -54,7 +56,8 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities
|
|||
{
|
||||
var withMethod = typeof(TDependencies).GetTypeInfo().DeclaredMethods
|
||||
.Single(
|
||||
m => m.Name == "With"
|
||||
m => m.CustomAttributes.All(a => a.AttributeType != typeof(ObsoleteAttribute))
|
||||
&& m.Name == "With"
|
||||
&& m.GetParameters()[0].ParameterType == serviceType);
|
||||
|
||||
var clone = withMethod.Invoke(dependencies, new[] { services2.GetService(serviceType) });
|
||||
|
@ -189,7 +192,7 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities
|
|||
{
|
||||
var contextServices = CreateContextServices();
|
||||
|
||||
var conventionSetBuilder = contextServices.GetRequiredService<IConventionSetBuilder>();
|
||||
var conventionSetBuilder = new CompositeConventionSetBuilder(contextServices.GetRequiredService<IEnumerable<IConventionSetBuilder>>().ToList());
|
||||
var conventionSet = contextServices.GetRequiredService<ICoreConventionSetBuilder>().CreateConventionSet();
|
||||
conventionSet = conventionSetBuilder == null
|
||||
? conventionSet
|
||||
|
|
|
@ -104,7 +104,6 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
|
|||
{ typeof(IChangeDetector), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IDbContextServices), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IValueGeneratorSelector), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IConventionSetBuilder), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IExpressionPrinter), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IExecutionStrategyFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IAsyncQueryProvider), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
|
@ -127,6 +126,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
|
|||
{ typeof(IEntityQueryableExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IEntityQueryModelVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(ILazyLoader), new ServiceCharacteristics(ServiceLifetime.Scoped) },
|
||||
{ typeof(IConventionSetBuilder), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
|
||||
{ typeof(IEntityStateListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
|
||||
{ typeof(INavigationListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
|
||||
{ typeof(IKeyListener), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
|
||||
|
|
|
@ -347,16 +347,24 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
|
|||
|
||||
foreach (var entityType in model.GetEntityTypes())
|
||||
{
|
||||
var properties = new HashSet<IPropertyBase>(
|
||||
entityType
|
||||
.GetDeclaredProperties()
|
||||
.Cast<IPropertyBase>()
|
||||
.Concat(entityType.GetDeclaredNavigations())
|
||||
.Where(p => !p.IsShadowProperty));
|
||||
|
||||
var constructorBinding = (ConstructorBinding)entityType[CoreAnnotationNames.ConstructorBinding];
|
||||
|
||||
foreach (var propertyBase in entityType
|
||||
.GetDeclaredProperties()
|
||||
.Cast<IPropertyBase>()
|
||||
.Concat(entityType.GetDeclaredNavigations())
|
||||
.Where(
|
||||
e => !e.IsShadowProperty
|
||||
&& (constructorBinding == null
|
||||
|| constructorBinding.ParameterBindings.All(p => p.ConsumedProperty != e))))
|
||||
if (constructorBinding != null)
|
||||
{
|
||||
foreach (var consumedProperty in constructorBinding.ParameterBindings.SelectMany(p => p.ConsumedProperties))
|
||||
{
|
||||
properties.Remove(consumedProperty);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyBase in properties)
|
||||
{
|
||||
if (!propertyBase.TryGetMemberInfo(
|
||||
forConstruction: true,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
@ -70,7 +71,7 @@ namespace Microsoft.EntityFrameworkCore.Internal
|
|||
|
||||
return _scopedProvider.GetService<IModelSource>().GetModel(
|
||||
_currentContext.Context,
|
||||
_scopedProvider.GetService<IConventionSetBuilder>(),
|
||||
new CompositeConventionSetBuilder(_scopedProvider.GetService<IEnumerable<IConventionSetBuilder>>().ToList()),
|
||||
_scopedProvider.GetService<IModelValidator>());
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -57,12 +57,16 @@ namespace Microsoft.EntityFrameworkCore.Internal
|
|||
}
|
||||
else if (Context.ChangeTracker.LazyLoadingEnabled)
|
||||
{
|
||||
var entry = Context.Entry(entity).Navigation(navigationName);
|
||||
if (!entry.IsLoaded)
|
||||
var entityEntry = Context.Entry(entity);
|
||||
if (entityEntry.State != EntityState.Detached)
|
||||
{
|
||||
Logger.NavigationLazyLoading(Context, entity, navigationName);
|
||||
var entry = entityEntry.Navigation(navigationName);
|
||||
if (!entry.IsLoaded)
|
||||
{
|
||||
Logger.NavigationLazyLoading(Context, entity, navigationName);
|
||||
|
||||
entry.Load();
|
||||
entry.Load();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class CompositeConventionSetBuilder : IConventionSetBuilder
|
||||
{
|
||||
private readonly IReadOnlyList<IConventionSetBuilder> _builders;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public CompositeConventionSetBuilder([NotNull] IReadOnlyList<IConventionSetBuilder> builders)
|
||||
{
|
||||
_builders = builders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual ConventionSet AddConventions(ConventionSet conventionSet)
|
||||
{
|
||||
foreach (var builder in _builders)
|
||||
{
|
||||
builder.AddConventions(conventionSet);
|
||||
}
|
||||
|
||||
return conventionSet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public ContextParameterBinding([NotNull] Type contextType)
|
||||
: base(contextType, null)
|
||||
: base(contextType)
|
||||
{
|
||||
ContextType = contextType;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType enityType, ParameterInfo parameter)
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType entityType, ParameterInfo parameter)
|
||||
=> typeof(DbContext).IsAssignableFrom(parameter.ParameterType)
|
||||
? new ContextParameterBinding(parameter.ParameterType)
|
||||
: null;
|
||||
|
|
|
@ -178,10 +178,21 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
constructorBinding.CreateConstructorExpression(bindingInfo))
|
||||
};
|
||||
|
||||
var properties = new HashSet<IProperty>(
|
||||
entityType
|
||||
.GetProperties()
|
||||
.Where(p => !p.IsShadowProperty));
|
||||
|
||||
foreach (var consumedProperty in constructorBinding
|
||||
.ParameterBindings
|
||||
.SelectMany(p => p.ConsumedProperties)
|
||||
.OfType<IProperty>())
|
||||
{
|
||||
properties.Remove(consumedProperty);
|
||||
}
|
||||
|
||||
blockExpressions.AddRange(
|
||||
from property in entityType.GetProperties().Where(
|
||||
p => !p.IsShadowProperty
|
||||
&& constructorBinding.ParameterBindings.All(b => b.ConsumedProperty != p))
|
||||
from property in properties
|
||||
let targetMember = Expression.MakeMemberAccess(
|
||||
instanceVariable,
|
||||
property.GetMemberInfo(forConstruction: true, forSet: true))
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public EntityTypeParameterBinding()
|
||||
: base(typeof(IEntityType), null)
|
||||
: base(typeof(IEntityType))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,6 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Expression BindToParameter(ParameterBindingInfo bindingInfo)
|
||||
=> Expression.Constant(bindingInfo.EnityType, typeof(IEntityType));
|
||||
=> Expression.Constant(bindingInfo.EntityType, typeof(IEntityType));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType enityType, ParameterInfo parameter)
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType entityType, ParameterInfo parameter)
|
||||
=> parameter.ParameterType == typeof(IEntityType)
|
||||
? new EntityTypeParameterBinding()
|
||||
: null;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
@ -62,9 +61,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
_factoryMethod,
|
||||
arguments);
|
||||
|
||||
if (_factoryMethod.ReturnType != bindingInfo.EnityType.ClrType)
|
||||
if (_factoryMethod.ReturnType != bindingInfo.EntityType.ClrType)
|
||||
{
|
||||
expression = Expression.Convert(expression, bindingInfo.EnityType.ClrType);
|
||||
expression = Expression.Convert(expression, bindingInfo.EntityType.ClrType);
|
||||
}
|
||||
|
||||
return expression;
|
||||
|
|
|
@ -18,11 +18,11 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType enityType, ParameterInfo parameter)
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType entityType, ParameterInfo parameter)
|
||||
{
|
||||
if (parameter.ParameterType == typeof(ILazyLoader))
|
||||
{
|
||||
EnsureFieldAccess(enityType);
|
||||
EnsureFieldAccess(entityType);
|
||||
|
||||
return new ServiceParameterBinding(typeof(ILazyLoader), typeof(ILazyLoader));
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
if (parameter.ParameterType == typeof(Action<object, string>)
|
||||
&& parameter.Name.Equals("lazyLoader", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
EnsureFieldAccess(enityType);
|
||||
EnsureFieldAccess(entityType);
|
||||
|
||||
return new ServiceMethodParameterBinding(typeof(Action<object, string>), typeof(ILazyLoader), _loadMethod);
|
||||
}
|
||||
|
@ -38,9 +38,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
return null;
|
||||
}
|
||||
|
||||
private static void EnsureFieldAccess(IMutableEntityType enityType)
|
||||
private static void EnsureFieldAccess(IMutableEntityType entityType)
|
||||
{
|
||||
foreach (var navigation in enityType.GetNavigations())
|
||||
foreach (var navigation in entityType.GetNavigations())
|
||||
{
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class ObjectArrayParameterBinding : ParameterBinding
|
||||
{
|
||||
private readonly IReadOnlyList<ParameterBinding> _bindings;
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public ObjectArrayParameterBinding([NotNull] IReadOnlyList<ParameterBinding> bindings)
|
||||
: base(typeof(object[]), bindings.SelectMany(b => b.ConsumedProperties).ToArray())
|
||||
{
|
||||
_bindings = bindings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Expression BindToParameter(ParameterBindingInfo bindingInfo)
|
||||
=> Expression.NewArrayInit(
|
||||
typeof(object),
|
||||
_bindings.Select(
|
||||
b =>
|
||||
{
|
||||
var expression = b.BindToParameter(bindingInfo);
|
||||
|
||||
if (expression.Type.GetTypeInfo().IsValueType)
|
||||
{
|
||||
expression = Expression.Convert(expression, typeof(object));
|
||||
}
|
||||
|
||||
return expression;
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
|
@ -19,10 +20,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// </summary>
|
||||
protected ParameterBinding(
|
||||
[NotNull] Type parameterType,
|
||||
[CanBeNull] IPropertyBase consumedProperty)
|
||||
[NotNull] params IPropertyBase[] consumedProperties)
|
||||
{
|
||||
ParameterType = parameterType;
|
||||
ConsumedProperty = consumedProperty;
|
||||
ConsumedProperties = consumedProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -35,7 +36,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual IPropertyBase ConsumedProperty { get; }
|
||||
public virtual IReadOnlyList<IPropertyBase> ConsumedProperties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public abstract ParameterBinding TryBindParameter(
|
||||
[NotNull] IMutableEntityType enityType,
|
||||
[NotNull] IMutableEntityType entityType,
|
||||
[NotNull] ParameterInfo parameter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,13 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public ParameterBindingInfo(
|
||||
[NotNull] IEntityType enityType,
|
||||
[NotNull] IEntityType entityType,
|
||||
[NotNull] Expression valueBufferExpression,
|
||||
[NotNull] Expression contextExpression,
|
||||
[CanBeNull] int[] indexMap)
|
||||
{
|
||||
_indexMap = indexMap;
|
||||
EnityType = enityType;
|
||||
EntityType = entityType;
|
||||
ValueBufferExpression = valueBufferExpression;
|
||||
ContextExpression = contextExpression;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public IEntityType EnityType { get; }
|
||||
public IEntityType EntityType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
|
|
|
@ -26,10 +26,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override Expression BindToParameter(ParameterBindingInfo bindingInfo)
|
||||
=> Expression.Call(
|
||||
EntityMaterializerSource.TryReadValueMethod.MakeGenericMethod(ConsumedProperty.ClrType),
|
||||
{
|
||||
var property = ConsumedProperties[0];
|
||||
|
||||
return Expression.Call(
|
||||
EntityMaterializerSource.TryReadValueMethod.MakeGenericMethod(property.ClrType),
|
||||
bindingInfo.ValueBufferExpression,
|
||||
Expression.Constant(bindingInfo.GetValueBufferIndex(ConsumedProperty)),
|
||||
Expression.Constant(ConsumedProperty, typeof(IPropertyBase)));
|
||||
Expression.Constant(bindingInfo.GetValueBufferIndex(property)),
|
||||
Expression.Constant(property, typeof(IPropertyBase)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,11 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType enityType, ParameterInfo parameter)
|
||||
public override ParameterBinding TryBindParameter(IMutableEntityType entityType, ParameterInfo parameter)
|
||||
{
|
||||
var candidateNames = GetCandidatePropertyNames(parameter);
|
||||
|
||||
return enityType.GetProperties().Where(
|
||||
return entityType.GetProperties().Where(
|
||||
p => p.ClrType == parameter.ParameterType
|
||||
&& candidateNames.Any(c => c.Equals(p.Name, StringComparison.Ordinal)))
|
||||
.Select(p => new PropertyParameterBinding(p)).FirstOrDefault();
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
public ServiceParameterBinding(
|
||||
[NotNull] Type parameterType,
|
||||
[NotNull] Type serviceType)
|
||||
: base(parameterType, null)
|
||||
: base(parameterType)
|
||||
{
|
||||
ServiceType = serviceType;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public class LazyLoadProxyInMemoryTest : LazyLoadProxyTestBase<LazyLoadProxyInMemoryTest.LoadInMemoryFixture>
|
||||
{
|
||||
public LazyLoadProxyInMemoryTest(LoadInMemoryFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
public class LoadInMemoryFixture : LoadFixtureBase
|
||||
{
|
||||
protected override ITestStoreFactory TestStoreFactory => InMemoryTestStoreFactory.Instance;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public class ApiConsistencyTest : ApiConsistencyTestBase
|
||||
{
|
||||
protected override void AddServices(ServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddEntityFrameworkInMemoryDatabase();
|
||||
}
|
||||
|
||||
protected override Assembly TargetAssembly => typeof(ProxiesExtensions).GetTypeInfo().Assembly;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
<AssemblyName>Microsoft.EntityFrameworkCore.Proxies.Tests</AssemblyName>
|
||||
<RootNamespace>Microsoft.EntityFrameworkCore</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\xunit.runner.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\EFCore.Proxies\EFCore.Proxies.csproj" />
|
||||
<ProjectReference Include="..\EFCore.Tests\EFCore.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="TestUtilities\FakeProvider\FakeDbCommand.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,456 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Castle.DynamicProxy;
|
||||
using Castle.DynamicProxy.Generators;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public class LazyLoadingProxyTests
|
||||
{
|
||||
[Fact]
|
||||
public void Materialization_uses_parameterless_constructor()
|
||||
{
|
||||
using (var context = new NeweyContext(nameof(Materialization_uses_parameterless_constructor)))
|
||||
{
|
||||
context.Add(new March82GGtp());
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(nameof(Materialization_uses_parameterless_constructor)))
|
||||
{
|
||||
Assert.Same(typeof(March82GGtp), context.Set<March82GGtp>().Single().GetType().BaseType);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Materialization_uses_parameterized_constructor()
|
||||
{
|
||||
using (var context = new NeweyContext(nameof(Materialization_uses_parameterized_constructor)))
|
||||
{
|
||||
context.Add(new March881(77, "Leyton House"));
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(nameof(Materialization_uses_parameterized_constructor)))
|
||||
{
|
||||
var proxy = context.Set<March881>().Single();
|
||||
|
||||
Assert.Same(typeof(March881), proxy.GetType().BaseType);
|
||||
Assert.Equal(77, proxy.Id);
|
||||
Assert.Equal("Leyton House", proxy.Sponsor);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Materialization_uses_parameterized_constructor_taking_context()
|
||||
{
|
||||
using (var context = new NeweyContext(nameof(Materialization_uses_parameterized_constructor_taking_context)))
|
||||
{
|
||||
context.Add(new WilliamsFw14(context, 6, "Canon"));
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(nameof(Materialization_uses_parameterized_constructor_taking_context)))
|
||||
{
|
||||
var proxy = context.Set<WilliamsFw14>().Single();
|
||||
|
||||
Assert.Same(typeof(WilliamsFw14), proxy.GetType().BaseType);
|
||||
Assert.Same(context, proxy.Context);
|
||||
Assert.Equal(6, proxy.Id);
|
||||
Assert.Equal("Canon", proxy.Sponsor);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateProxy_uses_parameterless_constructor()
|
||||
{
|
||||
using (var context = new NeweyContext())
|
||||
{
|
||||
Assert.Same(typeof(March82GGtp), context.CreateProxy<March82GGtp>().GetType().BaseType);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateProxy_uses_parameterized_constructor()
|
||||
{
|
||||
using (var context = new NeweyContext())
|
||||
{
|
||||
var proxy = context.CreateProxy<March881>(77, "Leyton House");
|
||||
|
||||
Assert.Same(typeof(March881), proxy.GetType().BaseType);
|
||||
Assert.Equal(77, proxy.Id);
|
||||
Assert.Equal("Leyton House", proxy.Sponsor);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateProxy_uses_parameterized_constructor_taking_context()
|
||||
{
|
||||
using (var context = new NeweyContext())
|
||||
{
|
||||
var proxy = context.CreateProxy<WilliamsFw14>(context, 6, "Canon");
|
||||
|
||||
Assert.Same(typeof(WilliamsFw14), proxy.GetType().BaseType);
|
||||
Assert.Same(context, proxy.Context);
|
||||
Assert.Equal(6, proxy.Id);
|
||||
Assert.Equal("Canon", proxy.Sponsor);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Proxies_only_created_if_Use_called()
|
||||
{
|
||||
using (var context = new NeweyContext(nameof(Proxies_only_created_if_Use_called), false))
|
||||
{
|
||||
context.Add(new March82GGtp());
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(nameof(Proxies_only_created_if_Use_called), false))
|
||||
{
|
||||
Assert.Same(typeof(March82GGtp), context.Set<March82GGtp>().Single().GetType());
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(nameof(Proxies_only_created_if_Use_called)))
|
||||
{
|
||||
Assert.Same(typeof(March82GGtp), context.Set<March82GGtp>().Single().GetType().BaseType);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Proxy_services_must_be_available()
|
||||
{
|
||||
var withoutProxies = new ServiceCollection()
|
||||
.AddEntityFrameworkInMemoryDatabase()
|
||||
.BuildServiceProvider();
|
||||
|
||||
var withProxies = new ServiceCollection()
|
||||
.AddEntityFrameworkInMemoryDatabase()
|
||||
.AddEntityFrameworkProxies()
|
||||
.BuildServiceProvider();
|
||||
|
||||
using (var context = new NeweyContext(withoutProxies, nameof(Proxy_services_must_be_available), false))
|
||||
{
|
||||
context.Add(new March82GGtp());
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(withoutProxies, nameof(Proxy_services_must_be_available), false))
|
||||
{
|
||||
Assert.Same(typeof(March82GGtp), context.Set<March82GGtp>().Single().GetType());
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(withProxies, nameof(Proxy_services_must_be_available)))
|
||||
{
|
||||
Assert.Same(typeof(March82GGtp), context.Set<March82GGtp>().Single().GetType().BaseType);
|
||||
}
|
||||
|
||||
using (var context = new NeweyContext(withoutProxies, nameof(Proxy_services_must_be_available)))
|
||||
{
|
||||
Assert.Equal(
|
||||
ProxiesStrings.ProxyServicesMissing,
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.Model).Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_sealed_class()
|
||||
{
|
||||
using (var context = new NeweyContextN1())
|
||||
{
|
||||
Assert.Equal(
|
||||
ProxiesStrings.ItsASeal(nameof(McLarenMp418)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.Model).Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_non_virtual_navigation()
|
||||
{
|
||||
using (var context = new NeweyContextN2())
|
||||
{
|
||||
Assert.Equal(
|
||||
ProxiesStrings.NonVirtualNavigation(nameof(McLarenMp419.SelfRef), nameof(McLarenMp419)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.Model).Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_no_field_found()
|
||||
{
|
||||
using (var context = new NeweyContextN3())
|
||||
{
|
||||
Assert.Equal(
|
||||
CoreStrings.NoBackingFieldLazyLoading(nameof(MarchCg901.SelfRef), nameof(MarchCg901)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.Model).Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_type_not_available_to_Castle()
|
||||
{
|
||||
using (var context = new NeweyContextN4())
|
||||
{
|
||||
Assert.Throws<GeneratorException>(() => context.CreateProxy<McLarenMp421>());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_constructor_not_available_to_Castle()
|
||||
{
|
||||
using (var context = new NeweyContextN5())
|
||||
{
|
||||
Assert.Throws<InvalidProxyConstructorArgumentsException>(() => context.CreateProxy<RedBullRb3>());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateProxy_throws_if_constructor_args_do_not_match()
|
||||
{
|
||||
using (var context = new NeweyContext())
|
||||
{
|
||||
Assert.Throws<InvalidProxyConstructorArgumentsException>(() => context.CreateProxy<March881>(77, 88));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateProxy_throws_if_wrong_number_of_constructor_args()
|
||||
{
|
||||
using (var context = new NeweyContext())
|
||||
{
|
||||
Assert.Throws<InvalidProxyConstructorArgumentsException>(() => context.CreateProxy<March881>(77, 88, 99));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_create_proxy_for_non_mapped_type()
|
||||
{
|
||||
using (var context = new NeweyContextN4())
|
||||
{
|
||||
Assert.Equal(
|
||||
CoreStrings.EntityTypeNotFound(nameof(March82GGtp)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.CreateProxy<March82GGtp>()).Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_create_proxy_when_proxies_not_used()
|
||||
{
|
||||
using (var context = new NeweyContextN6())
|
||||
{
|
||||
Assert.Equal(
|
||||
ProxiesStrings.ProxiesNotEnabled(nameof(RedBullRb3)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.CreateProxy<RedBullRb3>()).Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_if_create_proxy_when_proxies_not_enabled()
|
||||
{
|
||||
using (var context = new NeweyContextN7())
|
||||
{
|
||||
Assert.Equal(
|
||||
ProxiesStrings.ProxiesNotEnabled(nameof(RedBullRb3)),
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => context.CreateProxy<RedBullRb3>()).Message);
|
||||
}
|
||||
}
|
||||
|
||||
public class March82GGtp
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public class March881
|
||||
{
|
||||
public March881(int id, string sponsor)
|
||||
{
|
||||
Id = id;
|
||||
Sponsor = sponsor;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
public string Sponsor { get; }
|
||||
}
|
||||
|
||||
public class WilliamsFw14
|
||||
{
|
||||
public WilliamsFw14(DbContext context, int id, string sponsor)
|
||||
{
|
||||
Context = context;
|
||||
Id = id;
|
||||
Sponsor = sponsor;
|
||||
}
|
||||
|
||||
public DbContext Context { get; }
|
||||
public int Id { get; }
|
||||
public string Sponsor { get; }
|
||||
}
|
||||
|
||||
private class NeweyContext : DbContext
|
||||
{
|
||||
private readonly IServiceProvider _internalServiceProvider;
|
||||
private static readonly InMemoryDatabaseRoot _dbRoot = new InMemoryDatabaseRoot();
|
||||
private readonly bool _useProxies;
|
||||
private readonly string _dbName;
|
||||
|
||||
public NeweyContext(string dbName = null, bool useProxies = true)
|
||||
{
|
||||
_dbName = dbName;
|
||||
_useProxies = useProxies;
|
||||
}
|
||||
|
||||
public NeweyContext(IServiceProvider internalServiceProvider, string dbName = null, bool useProxies = true)
|
||||
: this(dbName, useProxies)
|
||||
{
|
||||
_internalServiceProvider = internalServiceProvider;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (_useProxies)
|
||||
{
|
||||
optionsBuilder.UseLazyLoadingProxies();
|
||||
}
|
||||
|
||||
if (_internalServiceProvider != null)
|
||||
{
|
||||
optionsBuilder.UseInternalServiceProvider(_internalServiceProvider);
|
||||
}
|
||||
|
||||
optionsBuilder.UseInMemoryDatabase(_dbName ?? nameof(NeweyContext), _dbRoot);
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<March82GGtp>();
|
||||
|
||||
modelBuilder.Entity<March881>(
|
||||
b =>
|
||||
{
|
||||
b.Property(e => e.Id);
|
||||
b.Property(e => e.Sponsor);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<WilliamsFw14>(
|
||||
b =>
|
||||
{
|
||||
b.Property(e => e.Id);
|
||||
b.Property(e => e.Sponsor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class McLarenMp418
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
private class NeweyContextN : DbContext
|
||||
{
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder
|
||||
.UseLazyLoadingProxies()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString());
|
||||
}
|
||||
|
||||
private class NeweyContextN1 : NeweyContextN
|
||||
{
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<McLarenMp418>();
|
||||
}
|
||||
|
||||
public class McLarenMp419
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public McLarenMp419 SelfRef { get; set; }
|
||||
}
|
||||
|
||||
private class NeweyContextN2 : NeweyContextN
|
||||
{
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<McLarenMp419>();
|
||||
}
|
||||
|
||||
public class MarchCg901
|
||||
{
|
||||
private MarchCg901 _hiddenBackingField;
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
// ReSharper disable once ConvertToAutoProperty
|
||||
public virtual MarchCg901 SelfRef
|
||||
{
|
||||
get => _hiddenBackingField;
|
||||
set => _hiddenBackingField = value;
|
||||
}
|
||||
}
|
||||
|
||||
private class NeweyContextN3 : NeweyContextN
|
||||
{
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<MarchCg901>();
|
||||
}
|
||||
|
||||
internal class McLarenMp421
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
private class NeweyContextN4 : NeweyContextN
|
||||
{
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<McLarenMp421>();
|
||||
}
|
||||
|
||||
public class RedBullRb3
|
||||
{
|
||||
internal RedBullRb3()
|
||||
{
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
private class NeweyContextN5 : NeweyContextN
|
||||
{
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<RedBullRb3>();
|
||||
}
|
||||
|
||||
private class NeweyContextN6 : DbContext
|
||||
{
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString());
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<March82GGtp>();
|
||||
}
|
||||
|
||||
private class NeweyContextN7 : DbContext
|
||||
{
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder
|
||||
.UseLazyLoadingProxies(false)
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString());
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
=> modelBuilder.Entity<March82GGtp>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,448 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public class LazyLoadProxySqlServerTest : LazyLoadProxyTestBase<LazyLoadProxySqlServerTest.LoadSqlServerFixture>
|
||||
{
|
||||
public LazyLoadProxySqlServerTest(LoadSqlServerFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
fixture.TestSqlLoggerFactory.Clear();
|
||||
}
|
||||
|
||||
public override void Lazy_load_collection(EntityState state)
|
||||
{
|
||||
base.Lazy_load_collection(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [Child] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_dependent(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_dependent(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [Single] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_PK_to_PK_reference_to_principal(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_PK_to_PK_reference_to_principal(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_PK_to_PK_reference_to_dependent(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_PK_to_PK_reference_to_dependent(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707'
|
||||
|
||||
SELECT [e].[Id]
|
||||
FROM [SinglePkToPk] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_null_FK(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_null_FK(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_null_FK(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_null_FK(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_collection_not_found(EntityState state)
|
||||
{
|
||||
base.Lazy_load_collection_not_found(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='767' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [Child] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_not_found(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_not_found(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='787'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_not_found(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_not_found(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='787'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_dependent_not_found(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_dependent_not_found(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='767' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [Single] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_collection_already_loaded(EntityState state)
|
||||
{
|
||||
base.Lazy_load_collection_already_loaded(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_already_loaded(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_already_loaded(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_already_loaded(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_already_loaded(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_dependent_already_loaded(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_dependent_already_loaded(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_PK_to_PK_reference_to_principal_already_loaded(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_PK_to_PK_reference_to_principal_already_loaded(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_PK_to_PK_reference_to_dependent_already_loaded(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_PK_to_PK_reference_to_dependent_already_loaded(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_alternate_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_alternate_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[AlternateId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_alternate_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_alternate_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[AlternateId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_dependent_alternate_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_dependent_alternate_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [SingleAk] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_null_FK_alternate_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_null_FK_alternate_key(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_null_FK_alternate_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_null_FK_alternate_key(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_collection_shadow_fk(EntityState state)
|
||||
{
|
||||
base.Lazy_load_collection_shadow_fk(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [ChildShadowFk] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_shadow_fk(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_shadow_fk(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_shadow_fk(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_shadow_fk(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE [e].[Id] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_dependent_shadow_fk(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_dependent_shadow_fk(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [SingleShadowFk] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_null_FK_shadow_fk(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_null_FK_shadow_fk(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_null_FK_shadow_fk(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_null_FK_shadow_fk(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_collection_composite_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_collection_composite_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
@__get_Item_1='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentAlternateId], [e].[ParentId]
|
||||
FROM [ChildCompositeKey] AS [e]
|
||||
WHERE ([e].[ParentAlternateId] = @__get_Item_0) AND ([e].[ParentId] = @__get_Item_1)",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_composite_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_composite_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
@__get_Item_1='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE ([e].[AlternateId] = @__get_Item_0) AND ([e].[Id] = @__get_Item_1)",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_composite_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_composite_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
@__get_Item_1='707'
|
||||
|
||||
SELECT [e].[Id], [e].[AlternateId]
|
||||
FROM [Parent] AS [e]
|
||||
WHERE ([e].[AlternateId] = @__get_Item_0) AND ([e].[Id] = @__get_Item_1)",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_dependent_composite_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_dependent_composite_key(state);
|
||||
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='Root' (Size = 450)
|
||||
@__get_Item_1='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentAlternateId], [e].[ParentId]
|
||||
FROM [SingleCompositeKey] AS [e]
|
||||
WHERE ([e].[ParentAlternateId] = @__get_Item_0) AND ([e].[ParentId] = @__get_Item_1)",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
public override void Lazy_load_many_to_one_reference_to_principal_null_FK_composite_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_many_to_one_reference_to_principal_null_FK_composite_key(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override void Lazy_load_one_to_one_reference_to_principal_null_FK_composite_key(EntityState state)
|
||||
{
|
||||
base.Lazy_load_one_to_one_reference_to_principal_null_FK_composite_key(state);
|
||||
|
||||
Assert.Equal("", Sql);
|
||||
}
|
||||
|
||||
public override async Task Load_collection(EntityState state, bool async)
|
||||
{
|
||||
await base.Load_collection(state, async);
|
||||
|
||||
if (!async)
|
||||
{
|
||||
Assert.Equal(
|
||||
@"@__get_Item_0='707' (Nullable = true)
|
||||
|
||||
SELECT [e].[Id], [e].[ParentId]
|
||||
FROM [Child] AS [e]
|
||||
WHERE [e].[ParentId] = @__get_Item_0",
|
||||
Sql,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
|
||||
|
||||
protected override void RecordLog() => Sql = Fixture.TestSqlLoggerFactory.Sql;
|
||||
|
||||
private string Sql { get; set; }
|
||||
|
||||
public class LoadSqlServerFixture : LoadFixtureBase
|
||||
{
|
||||
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance;
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
=> base.AddOptions(builder).ConfigureWarnings(
|
||||
c => c
|
||||
.Log(RelationalEventId.QueryClientEvaluationWarning));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public abstract class ProxyGraphUpdatesSqlServerTest
|
||||
{
|
||||
public abstract class ProxyGraphUpdatesSqlServerTestBase<TFixture> : ProxyGraphUpdatesTestBase<TFixture>
|
||||
where TFixture : ProxyGraphUpdatesSqlServerTestBase<TFixture>.ProxyGraphUpdatesSqlServerFixtureBase, new()
|
||||
{
|
||||
protected ProxyGraphUpdatesSqlServerTestBase(TFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
|
||||
=> facade.UseTransaction(transaction.GetDbTransaction());
|
||||
|
||||
public abstract class ProxyGraphUpdatesSqlServerFixtureBase : ProxyGraphUpdatesFixtureBase
|
||||
{
|
||||
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public class LazyLoading : ProxyGraphUpdatesSqlServerTestBase<LazyLoading.ProxyGraphUpdatesWithLazyLoadingSqlServerFixture>
|
||||
{
|
||||
public LazyLoading(ProxyGraphUpdatesWithLazyLoadingSqlServerFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
public class ProxyGraphUpdatesWithLazyLoadingSqlServerFixture : ProxyGraphUpdatesSqlServerFixtureBase
|
||||
{
|
||||
protected override string StoreName { get; } = "ProxyGraphLazyLoadingUpdatesTest";
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
=> base.AddOptions(builder.UseLazyLoadingProxies());
|
||||
|
||||
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
|
||||
=> base.AddServices(serviceCollection.AddEntityFrameworkProxies());
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
|
||||
{
|
||||
modelBuilder.ForSqlServerUseIdentityColumns();
|
||||
|
||||
base.OnModelCreating(modelBuilder, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public class LazyLoadProxySqliteTest : LazyLoadProxyTestBase<LazyLoadProxySqliteTest.LoadSqliteFixture>
|
||||
{
|
||||
public LazyLoadProxySqliteTest(LoadSqliteFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
public class LoadSqliteFixture : LoadFixtureBase
|
||||
{
|
||||
protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance;
|
||||
|
||||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
|
||||
=> base.AddOptions(builder).ConfigureWarnings(
|
||||
c => c
|
||||
.Log(RelationalEventId.QueryClientEvaluationWarning));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
|
@ -43,9 +44,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("shadow", parameters[1].Name);
|
||||
Assert.Equal("id", parameters[2].Name);
|
||||
|
||||
Assert.Equal("Title", bindings[0].ConsumedProperty.Name);
|
||||
Assert.Equal("Shadow", bindings[1].ConsumedProperty.Name);
|
||||
Assert.Equal("Id", bindings[2].ConsumedProperty.Name);
|
||||
Assert.Equal("Title", bindings[0].ConsumedProperties.First().Name);
|
||||
Assert.Equal("Shadow", bindings[1].ConsumedProperties.First().Name);
|
||||
Assert.Equal("Id", bindings[2].ConsumedProperties.First().Name);
|
||||
}
|
||||
|
||||
private class BlogSeveral : Blog
|
||||
|
@ -120,15 +121,15 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("FooBaar5", parameters[7].Name);
|
||||
Assert.Equal("FooBaar6", parameters[8].Name);
|
||||
|
||||
Assert.Equal("FooBaar1", bindings[0].ConsumedProperty.Name);
|
||||
Assert.Equal("fooBaar2", bindings[1].ConsumedProperty.Name);
|
||||
Assert.Equal("_fooBaar3", bindings[2].ConsumedProperty.Name);
|
||||
Assert.Equal("m_fooBaar4", bindings[3].ConsumedProperty.Name);
|
||||
Assert.Equal("_FooBaar5", bindings[4].ConsumedProperty.Name);
|
||||
Assert.Equal("m_FooBaar6", bindings[5].ConsumedProperty.Name);
|
||||
Assert.Equal("FooBaar1", bindings[6].ConsumedProperty.Name);
|
||||
Assert.Equal("_FooBaar5", bindings[7].ConsumedProperty.Name);
|
||||
Assert.Equal("m_FooBaar6", bindings[8].ConsumedProperty.Name);
|
||||
Assert.Equal("FooBaar1", bindings[0].ConsumedProperties.First().Name);
|
||||
Assert.Equal("fooBaar2", bindings[1].ConsumedProperties.First().Name);
|
||||
Assert.Equal("_fooBaar3", bindings[2].ConsumedProperties.First().Name);
|
||||
Assert.Equal("m_fooBaar4", bindings[3].ConsumedProperties.First().Name);
|
||||
Assert.Equal("_FooBaar5", bindings[4].ConsumedProperties.First().Name);
|
||||
Assert.Equal("m_FooBaar6", bindings[5].ConsumedProperties.First().Name);
|
||||
Assert.Equal("FooBaar1", bindings[6].ConsumedProperties.First().Name);
|
||||
Assert.Equal("_FooBaar5", bindings[7].ConsumedProperties.First().Name);
|
||||
Assert.Equal("m_FooBaar6", bindings[8].ConsumedProperties.First().Name);
|
||||
}
|
||||
|
||||
private class BlogSpanner : Blog
|
||||
|
@ -166,8 +167,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("content", parameters[0].Name);
|
||||
Assert.Equal("follows", parameters[1].Name);
|
||||
|
||||
Assert.Equal("_content", bindings[0].ConsumedProperty.Name);
|
||||
Assert.Equal("m_follows", bindings[1].ConsumedProperty.Name);
|
||||
Assert.Equal("_content", bindings[0].ConsumedProperties.First().Name);
|
||||
Assert.Equal("m_follows", bindings[1].ConsumedProperties.First().Name);
|
||||
}
|
||||
|
||||
private class BlogWierdScience : Blog
|
||||
|
@ -194,10 +195,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("context", parameters[1].Name);
|
||||
|
||||
Assert.IsType<PropertyParameterBinding>(bindings[0]);
|
||||
Assert.Equal("Id", bindings[0].ConsumedProperty.Name);
|
||||
Assert.Equal("Id", bindings[0].ConsumedProperties.First().Name);
|
||||
|
||||
Assert.IsType<ContextParameterBinding>(bindings[1]);
|
||||
Assert.Null(bindings[1].ConsumedProperty);
|
||||
Assert.Empty(bindings[1].ConsumedProperties);
|
||||
Assert.Same(typeof(DbContext), ((ContextParameterBinding)bindings[1]).ContextType);
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("context", parameters[0].Name);
|
||||
|
||||
Assert.IsType<ContextParameterBinding>(bindings[0]);
|
||||
Assert.Null(bindings[0].ConsumedProperty);
|
||||
Assert.Empty(bindings[0].ConsumedProperties);
|
||||
Assert.Same(typeof(TypedContext), ((ContextParameterBinding)bindings[0]).ContextType);
|
||||
}
|
||||
|
||||
|
@ -251,7 +252,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("loader", parameters[0].Name);
|
||||
|
||||
Assert.IsType<ServiceParameterBinding>(bindings[0]);
|
||||
Assert.Null(bindings[0].ConsumedProperty);
|
||||
Assert.Empty(bindings[0].ConsumedProperties);
|
||||
Assert.Same(typeof(ILazyLoader), ((ServiceParameterBinding)bindings[0]).ServiceType);
|
||||
}
|
||||
|
||||
|
@ -278,7 +279,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("lazyLoader", parameters[0].Name);
|
||||
|
||||
Assert.IsType<ServiceMethodParameterBinding>(bindings[0]);
|
||||
Assert.Null(bindings[0].ConsumedProperty);
|
||||
Assert.Empty(bindings[0].ConsumedProperties);
|
||||
Assert.Same(typeof(ILazyLoader), ((ServiceMethodParameterBinding)bindings[0]).ServiceType);
|
||||
}
|
||||
|
||||
|
@ -305,7 +306,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
Assert.Equal("entityType", parameters[0].Name);
|
||||
|
||||
Assert.IsType<EntityTypeParameterBinding>(bindings[0]);
|
||||
Assert.Null(bindings[0].ConsumedProperty);
|
||||
Assert.Empty(bindings[0].ConsumedProperties);
|
||||
}
|
||||
|
||||
private class BlogWithEntityType : Blog
|
||||
|
|
|
@ -702,7 +702,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
|
|||
private ModelBuilder CreateModelBuilder()
|
||||
{
|
||||
var contextServices = InMemoryTestHelpers.Instance.CreateContextServices();
|
||||
return new ModelBuilder(contextServices.GetRequiredService<IConventionSetBuilder>()
|
||||
return new ModelBuilder(new CompositeConventionSetBuilder(contextServices.GetRequiredService<IEnumerable<IConventionSetBuilder>>().ToList())
|
||||
.AddConventions(new CoreConventionSetBuilder(
|
||||
contextServices.GetRequiredService<CoreConventionSetBuilderDependencies>().With(CreateLogger()))
|
||||
.CreateConventionSet()));
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
|
||||
entityType[CoreAnnotationNames.ConstructorBinding]
|
||||
= new FactoryMethodConstructorBinding(
|
||||
typeof(SomeEntity).GetTypeInfo().GetDeclaredMethod("Factory"),
|
||||
typeof(SomeEntity).GetTypeInfo().GetDeclaredMethod(nameof(SomeEntity.Factory)),
|
||||
new List<ParameterBinding>
|
||||
{
|
||||
new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Id))),
|
||||
|
@ -82,6 +82,42 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
Assert.False(entity.GooSetterCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_create_materializer_for_entity_with_factory_method_with_object_array()
|
||||
{
|
||||
var entityType = CreateEntityType();
|
||||
|
||||
entityType[CoreAnnotationNames.ConstructorBinding]
|
||||
= new FactoryMethodConstructorBinding(
|
||||
typeof(SomeEntity).GetTypeInfo().GetDeclaredMethod(nameof(SomeEntity.GeneralFactory)),
|
||||
new List<ParameterBinding>
|
||||
{
|
||||
new ObjectArrayParameterBinding(
|
||||
new List<ParameterBinding>
|
||||
{
|
||||
new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Id))),
|
||||
new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Goo)))
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
var factory = GetMaterializer(new EntityMaterializerSource(), entityType);
|
||||
|
||||
var gu = Guid.NewGuid();
|
||||
var entity = (SomeEntity)factory(new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, SomeEnum.EnumValue }), null);
|
||||
|
||||
Assert.Equal(77, entity.Id);
|
||||
Assert.Equal("Fu", entity.Foo);
|
||||
Assert.Equal(gu, entity.Goo);
|
||||
Assert.Equal(SomeEnum.EnumValue, entity.Enum);
|
||||
Assert.Equal(SomeEnum.EnumValue, entity.MaybeEnum);
|
||||
|
||||
Assert.True(entity.FactoryUsed);
|
||||
Assert.True(entity.ParameterizedConstructorUsed);
|
||||
Assert.False(entity.IdSetterCalled);
|
||||
Assert.False(entity.GooSetterCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_create_materializer_for_entity_with_instance_factory_method()
|
||||
{
|
||||
|
@ -253,6 +289,13 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
|
|||
public static SomeEntity Factory(int id, Guid? goo)
|
||||
=> new SomeEntity(id, goo) { FactoryUsed = true };
|
||||
|
||||
public static SomeEntity GeneralFactory(object[] constructorArguments)
|
||||
{
|
||||
Assert.Equal(2, constructorArguments.Length);
|
||||
|
||||
return Factory((int)constructorArguments[0], (Guid?)constructorArguments[1]);
|
||||
}
|
||||
|
||||
public bool FactoryUsed { get; set; }
|
||||
public bool ParameterizedConstructorUsed { get; set; }
|
||||
public bool IdSetterCalled { get; set; }
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
@ -144,7 +145,8 @@ namespace Microsoft.EntityFrameworkCore.ModelBuilding
|
|||
|
||||
var contextServices = testHelpers.CreateContextServices();
|
||||
|
||||
ModelBuilder = new ModelBuilder(contextServices.GetRequiredService<IConventionSetBuilder>().AddConventions(new CoreConventionSetBuilder(
|
||||
ModelBuilder = new ModelBuilder(new CompositeConventionSetBuilder(contextServices.GetRequiredService<IEnumerable<IConventionSetBuilder>>().ToList())
|
||||
.AddConventions(new CoreConventionSetBuilder(
|
||||
contextServices.GetRequiredService<CoreConventionSetBuilderDependencies>().With(modelLogger))
|
||||
.CreateConventionSet()));
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче