Skip to content

Commit

Permalink
Switch to new EF Core plugin model
Browse files Browse the repository at this point in the history
Notes:

* Added some missing NTS translations
* The new EF Core plugin model doesn't yet support specifying
  evaluatable (dotnet/efcore#13454),
  so we currently hack that up inside the main provider using type
  names as strings.

Fixes npgsql#658
  • Loading branch information
roji committed Oct 21, 2018
1 parent 981866b commit 2606034
Show file tree
Hide file tree
Showing 46 changed files with 1,858 additions and 1,190 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NetTopologySuite;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Design.Internal
{
public class NpgsqlNetTopologySuiteDesignTimeServices : IDesignTimeServices
{
public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
=> serviceCollection
.AddSingleton<IRelationalTypeMappingSourcePlugin, NpgsqlNetTopologySuiteTypeMappingSourcePlugin>()
.TryAddSingleton(NtsGeometryServices.Instance);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using GeoAPI;
using GeoAPI.Geometries;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Npgsql;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Utilities;

namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// NetTopologySuite specific extension methods for <see cref="NpgsqlDbContextOptionsBuilder"/>.
/// </summary>
// ReSharper disable once InconsistentNaming
public static class NpgsqlNetTopologySuiteDbContextOptionsBuilderExtensions
{
/// <summary>
/// Use NetTopologySuite to access SQL Server spatial data.
/// </summary>
/// <returns> The options builder so that further configuration can be chained. </returns>
public static NpgsqlDbContextOptionsBuilder UseNetTopologySuite(
[NotNull] this NpgsqlDbContextOptionsBuilder optionsBuilder,
ICoordinateSequenceFactory coordinateSequenceFactory = null,
IPrecisionModel precisionModel = null,
Ordinates handleOrdinates = Ordinates.None,
bool geographyAsDefault = false)
{
Check.NotNull(optionsBuilder, nameof(optionsBuilder));

// TODO: Do we really need this here or not? What does the SQL Server plugin do?
NetTopologySuiteBootstrapper.Bootstrap();

// TODO: Global-only setup at the ADO.NET level for now, optionally allow per-connection?
NpgsqlConnection.GlobalTypeMapper.UseNetTopologySuite(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault);

var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;

var extension = coreOptionsBuilder.Options.FindExtension<NpgsqlNetTopologySuiteOptionsExtension>()
?? new NpgsqlNetTopologySuiteOptionsExtension();

if (geographyAsDefault)
extension = extension.WithGeographyDefault();

((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);

return optionsBuilder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NetTopologySuite;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Utilities;

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite extension methods for <see cref="IServiceCollection" />.
/// </summary>
public static class NpgsqlNetTopologySuiteServiceCollectionExtensions
{
/// <summary>
/// Adds the services required for NetTopologySuite support in the Npgsql provider for Entity Framework.
/// </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 AddEntityFrameworkNpgsqlNetTopologySuite(
[NotNull] this IServiceCollection serviceCollection)
{
Check.NotNull(serviceCollection, nameof(serviceCollection));

new EntityFrameworkRelationalServicesBuilder(serviceCollection)
.TryAdd<ISingletonOptions, INpgsqlNetTopologySuiteOptions>(p => p.GetService<INpgsqlNetTopologySuiteOptions>())
.TryAddProviderSpecificServices(
x => x
.TryAddSingletonEnumerable<IRelationalTypeMappingSourcePlugin, NpgsqlNetTopologySuiteTypeMappingSourcePlugin>()
.TryAddSingletonEnumerable<IMethodCallTranslatorPlugin, NpgsqlNetTopologySuiteMethodCallTranslatorPlugin>()
.TryAddSingletonEnumerable<IMemberTranslatorPlugin, NpgsqlNetTopologySuiteMemberTranslatorPlugin>()
.TryAddSingleton<INpgsqlNetTopologySuiteOptions, NpgsqlNetTopologySuiteOptions>());


return serviceCollection;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal
{
/// <summary>
/// Represents options for Npgsql NetTopologySuite that can only be set at the <see cref="IServiceProvider"/> singleton level.
/// </summary>
public interface INpgsqlNetTopologySuiteOptions : ISingletonOptions
{
/// <summary>
/// True if geography is to be used by default instead of geometry
/// </summary>
bool IsGeographyDefault { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal
{
public class NpgsqlNetTopologySuiteOptionsExtension : IDbContextOptionsExtensionWithDebugInfo
{
bool _isGeographyDefault;
string _logFragment;

public NpgsqlNetTopologySuiteOptionsExtension() {}

protected NpgsqlNetTopologySuiteOptionsExtension([NotNull] NpgsqlNetTopologySuiteOptionsExtension copyFrom)
{
_isGeographyDefault = copyFrom._isGeographyDefault;
}

protected virtual NpgsqlNetTopologySuiteOptionsExtension Clone() => new NpgsqlNetTopologySuiteOptionsExtension(this);

public virtual bool ApplyServices(IServiceCollection services)
{
services.AddEntityFrameworkNpgsqlNetTopologySuite();

return false;
}

public virtual bool IsGeographyDefault => _isGeographyDefault;

public virtual NpgsqlNetTopologySuiteOptionsExtension WithGeographyDefault(bool isGeographyDefault = true)
{
var clone = Clone();

clone._isGeographyDefault = isGeographyDefault;

return clone;
}

public virtual long GetServiceProviderHashCode() => _isGeographyDefault ? 541 : 0;

public virtual void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
debugInfo["NpgsqlNetTopologySuite:" + nameof(IsGeographyDefault)]
= (_isGeographyDefault ? 541 : 0).ToString(CultureInfo.InvariantCulture);
}

public virtual void Validate(IDbContextOptions options)
{
var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
if (internalServiceProvider != null)
{
using (var scope = internalServiceProvider.CreateScope())
{
if (scope.ServiceProvider.GetService<IEnumerable<IRelationalTypeMappingSourcePlugin>>()
?.Any(s => s is NpgsqlNetTopologySuiteTypeMappingSourcePlugin) != true)
{
throw new InvalidOperationException("UseNetTopologySuite requires AddEntityFrameworkNpgsqlNetTopologySuite to be called on the internal service provider used.");
}
}
}
}

public virtual string LogFragment
{
get
{
if (_logFragment == null)
{
var builder = new StringBuilder("using NetTopologySuite");
if (_isGeographyDefault)
builder.Append(" (geography by default)");
builder.Append(' ');

_logFragment = builder.ToString();
}

return _logFragment;
}
}
}
}
24 changes: 24 additions & 0 deletions src/EFCore.PG.NTS/Internal/NpgsqlNetTopologySuiteOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Internal
{
/// <inheritdoc />
public class NpgsqlNetTopologySuiteOptions : INpgsqlNetTopologySuiteOptions
{
/// <inheritdoc />
public bool IsGeographyDefault { get; set; }

/// <inheritdoc />
public void Initialize(IDbContextOptions options)
{
var npgsqlNtsOptions = options.FindExtension<NpgsqlNetTopologySuiteOptionsExtension>() ?? new NpgsqlNetTopologySuiteOptionsExtension();

IsGeographyDefault = npgsqlNtsOptions.IsGeographyDefault;
}

/// <inheritdoc />
public void Validate(IDbContextOptions options) {}
}
}
32 changes: 0 additions & 32 deletions src/EFCore.PG.NTS/NetTopologySuiteDbContextOptionsExtensions.cs

This file was deleted.

46 changes: 0 additions & 46 deletions src/EFCore.PG.NTS/NetTopologySuiteGeographyTypeMapping.cs

This file was deleted.

46 changes: 0 additions & 46 deletions src/EFCore.PG.NTS/NetTopologySuiteGeometryTypeMapping.cs

This file was deleted.

Loading

0 comments on commit 2606034

Please sign in to comment.