I've attempted to use the new custom range type support with LocalTime (from NodaTime).
So I've set it up with:
services.AddDbContext<MainContext>(options =>
options.UseNpgsql(
Configuration.GetConnectionString("MainContext"),
opts =>
{
opts.UseNodaTime();
opts.MapRange<LocalTime>("timerange");
}));
and:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ForNpgsqlHasRange("timerange", "time");
// ...
}
Then in one of my model classes I've put:
public NpgsqlRange<LocalTime> Period { get; set; }
Then I ran:
dotnet ef migrations add Initial
and I get:
System.TypeLoadException: Could not load type 'Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping.NpgsqlRangeTypeMapping`1' from assembly 'Npgsql.EntityFrameworkCore.PostgreSQL, Version=2.2.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7'.
at Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.NodaTimePlugin..ctor()
at Microsoft.EntityFrameworkCore.NodaTimeDbContextOptionsExtensions.UseNodaTime(NpgsqlDbContextOptionsBuilder optionsBuilder) in /home/roji/projects/EFCore.PG/src/EFCore.PG.NodaTime/NodaTimeDbContextOptionsExtensions.cs:line 20
at Server.Startup.<>c.<ConfigureServices>b__4_2(NpgsqlDbContextOptionsBuilder opts)
These are the versions I'm using:
<PackageReference Include="NodaTime" Version="2.3.0" />
<PackageReference Include="Npgsql" Version="4.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0-preview1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="2.1.1" />
What am I doing wrong?
Not exactly sure, but I suspect it's related to using the preview version of the provider but not the preview version of the plugin (significant changes in this area for 2.2).
Try updating your versions and report back:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime --version 2.2.0-ci.1110 --source https://www.myget.org/F/npgsql-unstable/api/v3/index.json
@austindrenski I then get:
obj/Debug/netcoreapp2.1/EFCoreNpgsqlNodaTime.cs(10,12): error CS1729: 'DesignTimeServicesReferenceAttribute' does not contain a constructor that takes 2 arguments
Which I'm guessing is due to an ef version mismatch?
Tried with:
dotnet add package Microsoft.EntityFrameworkCore --version 2.2.0-preview3-35497
and now I get:
System.TypeLoadException: Could not load type 'Microsoft.EntityFrameworkCore.Infrastructure.IRelationalDbContextOptionsBuilderInfrastructure' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
at Microsoft.EntityFrameworkCore.NpgsqlNodaTimeDbContextOptionsBuilderExtensions.UseNodaTime(NpgsqlDbContextOptionsBuilder optionsBuilder)
at Server.Startup.<>c.b__4_3(NpgsqlDbContextOptionsBuilder opts) in /Users/shoe/Git/graph/src/Startup.cs:line 38
at Microsoft.EntityFrameworkCore.NpgsqlDbContextOptionsExtensions.UseNpgsql(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 npgsqlOptionsAction) in C:\projects\npgsql-entityframeworkcore-postgresqlsrc\EFCore.PG\Extensions\NpgsqlDbContextOptionsExtensions.cs:line 66
Hmm.... Yeah that looks like some type of mismatch is happening, but I haven't seen that particular one before.
Did you run a clean/restore cycle after updating?
If that doesn't work, we'll probably need to see some more code, or even a minimal repo to speculate further.
_edit: posted the above before your second response, see below for follow up._
I don't think you need to bring in the EF Core preview, as the proper preview is included by the PostgreSQL provider. Try rolling that back, and doing a full clean/restore/build cycle and report back.
Of 2.2.0-preview1-35029, 2.2.0-preview2-35157 and 2.2.0-preview3-35497 for Microsoft.EntityFrameworkCore the first two yield the DesignTimeServicesReferenceAttribute related error. The last one instead complains about IRelationalDbContextOptionsBuilderInfrastructure which was probably introduced in preview3?
@austindrenski I run a dotnet clean && dotnet restore yes, and nothing changed.
I think you can reproduce the issue by creating a new dotnet project and just define a model with a range for LocalTime following the first post.
I can indeed reproduce this locally with https://github.com/austindrenski/EFCore.PG-688.
Taking a look at the design time services now. It also looks like we don't have a preview build of the plugin, just a recent CI build in the unstable feed.
So the first level issue is the versions still need to be set to the unstable feed. @Jefffrey Could you try using the CI builds for both and report back?
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latest</LangVersion>
<OutputType>exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0-ci.1110" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="2.2.0-ci.1110" />
</ItemGroup>
</Project>
The second level issue is found in the repo:
```c#
namespace EFCore.PG_688
{
class Program
{
static void Main()
{
using (var ctx = new SomeContext())
{
try
{
ctx.Database.EnsureCreated();
Console.WriteLine(ctx.SomeModel.Count());
}
finally
{
ctx.Database.EnsureDeleted();
}
}
}
}
public class SomeModel
{
public int Id { get; set; }
public NpgsqlRange<LocalTime> Period { get; set; }
}
public class SomeContext : DbContext
{
public DbSet<SomeModel> SomeModel { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder builder)
=> builder.UseNpgsql(
"Host=localhost;Port=5432",
x => x.UseNodaTime().MapRange<LocalTime>("timerange", "time"));
protected override void OnModelCreating(ModelBuilder builder)
=> builder.ForNpgsqlHasRange("timerange", "time");
}
}
The surprising piece is this:
```c#
protected override void OnConfiguring(DbContextOptionsBuilder builder)
=> builder.UseNpgsql(
"Host=localhost;Port=5432",
x => x.UseNodaTime().MapRange<LocalTime>("timerange", "time"));
You should be able to omit the "time" parameter, but the type resolution doesn't seem to do the hop from actually the subtype should be resolved just by the mapping of the CLR timerange to NpgsqlRange<LocalTime> to the subtype.LocalTime type to the PostgreSQL time type.
It looks like the plugin's TypeMappingSource isn't loaded before MapRange<LocalTime>("timerange") attempts to resolve the subtype's mapping:
When the subtype is specified explicitly (e.g. "time") the CLR type isn't searched, giving the plugin's TypeMappingSource time to load before the handler is needed.
@roji Do you have a sense of how best we can affect this ordering?
@Jefffrey wrote:
@austindrenski I get:
warning NU1603: Server depends on Npgsql.EntityFrameworkCore.PostgreSQL (>= 2.2.0-ci.1110) but Npgsql.EntityFrameworkCore.PostgreSQL 2.2.0-ci.1110 was not found. An approximate best match of Npgsql.EntityFrameworkCore.PostgreSQL 2.2.0-preview1 was resolved.
You'll need to add a nuget.config to your project root with something like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" protocolVersion="3" />
<add key="npgsql" value="https://www.myget.org/F/npgsql/api/v3/index.json" protocolVersion="3" />
<add key="npgsql-unstable" value="https://www.myget.org/F/npgsql-unstable/api/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
@austindrenski I get:
System.Exception: Could not map range timerange, no mapping was found for subtype CLR type NodaTime.LocalTime
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlTypeMappingSource..ctor(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies, INpgsqlOptions npgsqlOptions) in C:\projects\npgsql-entityframeworkcore-postgresqlsrc\EFCore.PG\Storage\Internal\NpgsqlTypeMappingSource.cs:line 246
by having:
<PackageReference Include="NodaTime" Version="2.3.0" />
<PackageReference Include="Npgsql" Version="4.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0-rtm-35646" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0-ci.1110" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="2.2.0-ci.1110" />
@Jefffrey Yes, that's the exception I referenced above.
I'll try to take a look at this very soon.
@austindrenski OK, I took a look and your analysis seems correct... The user-defined range mapping setup process involves looking at NpgsqlTypeMappingSource's ClrTypeMapping dictionary, but that dictionary only includes the built-in mappings, and not the plugins.
I think the right way would be to move the user-defined range logic out of the constructor and into FindMapping(), probably by adding an additional FindUserDefinedRangeMapping() which gets called, just like FindArrayMapping(). Like FindArrayMapping(), if a mapping is instantiated, we can update NpgsqlTypeMappingSource's ClrTypeMapping and StoreTypeMapping dictionaries so that the next lookup simply finds it via FindExistingMapping(). Also, when looking for the subtype we definitely should not look at ClrTypeMapping, but rather recursively call FindMapping() which would include the plugins (again, exactly like FindArrayMapping() already does).
Are you interested in fixing this? If not I can probably do this quite quickly.
@roji That sounds good to me. It sounds like you have a specific implementation in mind, so no objections here! Happy to review once submitted.