How to do migrations with EF Core 2.2 using Spatial Data? - entity-framework

I am trying to do migrations in Entity Framework Core 2.2 but I get some strange errors.It should work since the documentation does not say anything about mapping code or so.
This command:
dotnet ef migrations add InitialCreate
results in this error:
The property 'Point.Boundary' is of an interface type ('IGeometry'). If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type, otherwise ignore the property using the NotMappedAttribute or 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I do not understand it. I have an enity, a context and all the required dependencies, including EF Core 2.2 . How do I solve it?
Project File:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0" />
<PackageReference Include="NetTopologySuite" Version="1.15.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.0.2105168" />
</ItemGroup>
Model File
using System.ComponentModel.DataAnnotations;
using NetTopologySuite.Geometries;
namespace WebApplication1.Models
{
public class Item
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Point Location { get; set; }
}
}
Context File
using System;
using Microsoft.EntityFrameworkCore;
using WebApplication1.Models;
namespace WebApplication1
{
public class ItemContext : DbContext
{
public ItemContext(DbContextOptions<ItemContext> options) : base(options)
{
Console.WriteLine("Context created");
}
public DbSet<Item> Items { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
Console.WriteLine("OnModelCreating");
}
}
}
The console:

Basically, the link you shared is a blog that only introduces the new features. At the end of every topic, you would find a link to the entire documentation. It seems like this suite requires an additional library based on which database you are using.
According to this documentation, you need to add the respective Spatial NuGet Package based on what your database is. (check the install section. For instance, add the Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite package if you are using SQL server. Once this is done, in your startup class, within your AddDbContext function, you can use something like this config.UseSqlServer("", x => x.UseNetTopologySuite()).

Related

TimerTrigger does not inject EF Database Context

I have an Azure Function (v3) using Entity Framework (3.0.11).
I am attempting to run the code on a TimerTrigger however injecting the database within a timer trigger does not seem to work.
Here are some (rapidly anonymized) code samples.
the CSPROJ
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AzureFunctions.Extensions.DependencyInjection" Version="1.1.3" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.11" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
a model and DBContext
namespace DataImport
{
public class Sample
{
public int SampleID { get; set; }
public string SampleField { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
public virtual DbSet<Sample> MyRecords { get; set; }
}
}
a startup class
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
[assembly: FunctionsStartup(typeof(DataImport.Startup))]
namespace DataImport
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
string con = builder.GetContext().Configuration.GetSection("ConnectionStrings:DefaultConnection").Value.ToString();
builder.Services.AddDbContext<MyDbContext>(config => config.UseSqlServer(con));
}
}
}
a Program.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using AzureFunctions.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace DataImport
{
public class Program
{
private readonly MyDbContext db;
public Program(MyDbContext database)
{
db = database;
}
[FunctionName("SampleFunction_works")]
public async Task<IActionResult> HttpRun([HttpTrigger(AuthorizationLevel.Anonymous, "GET")] HttpRequest req, ILogger log, ExecutionContext context)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
var foo = db.MyRecords.Where(c => c.SampleField == "000").FirstOrDefault();
await db.MyRecords.AddAsync(new Sample());
log.LogInformation(foo.SampleField);
return new OkObjectResult(foo);
}
[FunctionName("SampleFunction_no_work")]
public static void Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger log, ExecutionContext context)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
// tried dozens of things here, nothing works sofar.
// injecting IServiceProvider fails,
// what other ways to solve this?
// could a timer trigger perhaps make an HTTP call to the HttpRun function above?
}
}
}
when running the SampleFunction_works with a database connection we see the result of the function call as successful. Injection works within the context of an HTTP trigger. On a timertrigger however, this does not work.
I have tried a good 8 hours of different things at this point:
unsurprisingly accessing the db without injecting turns up a null property, no magic there.
adding MyDbContext to the Run function fails because it can't be injected public static void Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger log, MyDbContext db)
Microsoft.Azure.WebJobs.Host: Error indexing method 'SampleFunction_no_work'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'db' to type MyDbContext. Make sure the parameter Type is supported by the binding. If you're using binding MyDbContext(e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
doing the same as the previous but by adding IServiceProvider services to the method signature results in a similar error message, adding the line db = services.GetRequiredService<MyDbContext>(); is irrelevant if it can't get injected
some variables DO seem to be injectable in this scope ExecutionContext for example, but there doesn't seem to be anything I can use on that object.
Is there a way to:
inject a timer trigger with a database?
use a timer trigger to CALL an HTTPtrigger located within the same function?
any other solution that will allow me to access an EF database within a timertrigger context?
update:
#StevePy's comment below was correct. You can make a timertrigger's RUN method non-static and leverage the power of injection. I'd previously read that this wasn't possible, but it appears that information was out of date.
See this BLOG post for more info: https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/
Or grab this sample code to run for yourself locally:
[FunctionName("MY_FANCY_FUCNTION")]
public async Task Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger ilog, ExecutionContext context)
{
ilog.LogInformation($"TIMER EXECUTED IS DB NULL? '{db == null}'");
// note that the key part of this DOES log out as NOT NULL
// which is what we want.
return;
await Main(ilog, context);
}
Try using a non-static Run method. Many examples use a static method which can be recommended where you don't have dependencies and the method is pure. (since Functional methods should strive to be pure) See https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/ for an example of TimerTriggers /w DI.
#StevePy's comment below my questions was correct. You can make a timertrigger's RUN method non-static and leverage the power of injection. I'd previously read that this wasn't possible, but it appears that information was out of date.
See this BLOG post for more info: https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/
Or grab this sample code to run for yourself locally:
[FunctionName("MY_FANCY_FUCNTION")]
public async Task Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger ilog, ExecutionContext context)
{
ilog.LogInformation($"TIMER EXECUTED IS DB NULL? '{db == null}'");
// note that the key part of this DOES log out as NOT NULL
// which is what we want.
return;
await Main(ilog, context);
}
note that in the interests of giving credit where it is due, I'll switch the accepted answer to Steve's if and when he responds, but I'm marking as answered now to ensure that the question has an accepted answer.

No tables are created when using code first EF

So I'm trying to deploy a database on Azure using EF code first. I have one model:
public class User
{
[Key]
public int PersonID { get; set; }
[Required(ErrorMessage = "A first name is required.")]
[StringLength(20, MinimumLength = 2, ErrorMessage = "Your firstname needs to be atleast 2 letters long")]
[RegularExpression(#"^[a-zA-Z]*$", ErrorMessage = "Your firstname can only contain letters")]
[Display(Name = "First Name:")]
public string FirstName { get; set; }
}
DataContext-class:
public class DataContext : DbContext
{
public DataContext() : base("DataContext")
{
}
public DbSet<User> User { get; set; }
}
DataContextInitalizer-class:
public class DataContextInitializer : DropCreateDatabaseIfModelChanges<DataContext>
{
}
In the global.asax-file to initalize the datacontext:
Database.SetInitializer(new DataContextInitializer());
And then finally the string to connect to the database in the web.config:
<connectionStrings>
<add name="DataContext" connectionString="Data Source=tcp:*.database.windows.net,1433;Initial Catalog=TestApi20180311012458_db;User ID=usernamehere;Password=passwordhere" providerName="System.Data.SqlClient" />
Thou no tables are created when building the solution. I dont know where I'm missing out. Can you see whats wrong?
Thou no tables are created when building the solution. I dont know where I'm missing out. Can you see whats wrong?
Your code just to connect Azure sql in your local project. And the data initialization just uses to initialize data in table. But your tables have not created yet. If you want to create table in Azure sql, you could use Migrations.
Azure sql connection string in web.config:
<connectionStrings>
<add name="ConnectionStringName" providerName="System.Data.SqlClient" connectionString="Data Source=tcp:[databasename].database.windows.net,1433;Initial Catalog=[databasename];Integrated Security=False;User Id=[username];Password=[password];Encrypt=True;TrustServerCertificate=False;MultipleActiveResultSets=True" />
</connectionStrings>
Open Tools> Nuget Mackage Manager>Package Manager Console. Then enter the following code:
Enable-migrations
Add-migration initial
update-database
After that, you could see the tables are created in Azure Sql.
For another way, you could use publish tool to connect to Azure sql and use Code First Migrations automatically. Do not need to use code to configure manually. Open Publish settings to configure Azure sql. Or you could read this article to learn more details.

MvvmCross passing configuration to configurable plugin loader

I am creating a portable MockGeoLocationWatcher that one can substitute in place of the concrete implementations of IMvxGeoLocationWatcher until one has an actual device. This should facilitate development and testing of applications that require geo location.
The PluginLoader class for this plugin currently looks like this:
namespace Pidac.MvvmCross.Plugins.Location
{
public class PluginLoader : IMvxConfigurablePluginLoader
{
private bool _loaded;
public static readonly PluginLoader Instance = new PluginLoader();
public void EnsureLoaded()
{
if (_loaded)
return;
_loaded = true;
var locationWatcher = new MockGeoLocationWatcher();
var data = #"<?xml version='1.0' encoding='utf-8'?>
<WindowsPhoneEmulator xmlns='http://schemas.microsoft.com/WindowsPhoneEmulator/2009/08/SensorData'>
<SensorData>
<Header version='1' />
<GpsData latitude='48.619934106826' longitude='-84.5247359841114' />
<GpsData latitude='48.6852544862377' longitude='-83.9864059059864' />
<GpsData latitude='48.8445703681025' longitude='-83.7337203591114' />
<GpsData latitude='48.8662561090809' longitude='-83.2393355934864' />
<GpsData latitude='49.0825970371386' longitude='-83.0415816872364' />
<GpsData latitude='49.2621642999055' longitude='-82.7229781716114' />
<GpsData latitude='49.2621642999055' longitude='-82.6021285622364' />
<GpsData latitude='49.2047736379815' longitude='-82.3054977028614' />
</SensorData>
</WindowsPhoneEmulator>";
locationWatcher.SensorLocationData = data;
Mvx.RegisterSingleton(typeof(IMvxGeoLocationWatcher), locationWatcher);
}
public void Configure(IMvxPluginConfiguration configuration)
{
}
}
public class MockLocationWatcherConfiguration : IMvxPluginConfiguration
{
public static readonly MockLocationWatcherConfiguration Default = new MockLocationWatcherConfiguration();
// ideally, we should use this property to point to a file or string containing location data
// this should be configurable outside of code base.
public string SensorLocationData { get; set; }
}
}
I will like to pass the sensor data, currently hardcoded into the variable called "data" through an instance of MockLocationWatcherConfiguration but do not know where the MvvmCross framework is expecting to load the configuration for this plugin before IMvxConfigurablePluginLoader.Configure(configuration) is invoked. Ideally, I should specify this through configuration.
I looked at the Json plugin's implementation of PluginLoaded but still could not figure out where the configuration was retrieved before a cast was attempted in IMvxConfigurablePluginLoader.Configure.
Any ideas or pointers will be greatly appreciated.
TIA.
This is covered in the draft wiki page https://github.com/slodge/MvvmCross/wiki/MvvmCross-plugins - see "writing a configurable plugin"

Setting ConnectionTimeout when using EntityFramework

I would like to set the ConnectionTimeout to something other than the default, which is 15 seconds. I have inherited some code that uses EntityFramework and the app.config looks like this:
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS; Integrated Security=True; ConnectionTimeout=30; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=.\SQLEXPRESS; Integrated Security=True; ConnectionTimeout=30; MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
I'm the one who added the sectino in an attempt to get things working. I can tell it's not working be setting a breakpoint at:
var adapter = (IObjectContextAdapter) this;
var objectContext = adapter.ObjectContext;
objectContext.CommandTimeout = CommandTimeoutSeconds;
int test = objectContext.Connection.ConnectionTimeout;
test is always 15. What is going on? Can someone tell me how to set ConnectionTimeout? I have tried both "ConnectionTimeout" and "Connection Timeout" I.e. no space vs. space.
Can someone help me? I'm pulling my hair out. I'm sure it's a simple fix!
Dave
Additional info. In response to comment, here is my DbContext derived class...
public class SessionDataContext : DbContext
{
// Command timeout (seconds)
private const int CommandTimeoutSeconds = 30;
/// <summary>
/// Constructor that takes db name.
/// The connection string and db itself is configured in the this project's app.config file
/// </summary>
/// <param name="dbName"></param>
public SessionDataContext(string dbName) : base(dbName)
{
Database.SetInitializer(new SessionDataContextInitializer());
// Set timeout (based on code from http://stackoverflow.com/questions/6232633/entity-framework-timeouts)
var adapter = (IObjectContextAdapter) this;
var objectContext = adapter.ObjectContext;
objectContext.CommandTimeout = CommandTimeoutSeconds;
int test = objectContext.Connection.ConnectionTimeout;
}
/// <summary>
/// Session table's records
/// </summary>
public DbSet<Session> Sessions { get; set; }
/// <summary>
/// SessionType table's records
/// </summary>
public DbSet<SessionType> SessionTypes { get; set; }
}
It was stupidity on my part that was causing the problem! I put my answer here in case anyone in the future has this problem. Everything I typed above is correct and will work fine. However, the app.config file I was looking at was in a class library (our DataAccess layer). In fact, it was not being used at all and default EntityFramework settings were being used. I'm mot sure what led me to try it, but I moved the app.config settings from the DataAccess layer app.config to the main app.config and all worked beautifully. About all I can say in my defense other than I inherited the code is that it's not clear to me to see that the values in the app.config are not being used and one does not call them or use them in one's own code. Rather, MultipleActiveResultSets and ConnectionTimeout are used by the underlying Entity Framework.

'String cannot have zero length' error when using EF Tracing Data Provider

I am trying to incorporate 'EF Tracing Data Provider' into an existing MVC2 app using VS2010, .NET 4.0 in order to log all SQL commands. I have no interest at this time in the caching provider. I beleive I have followed all the steps listed in the blog posting. BLOG POST My project does compile without error, however when I attempt to run the project I get the following error:
'String cannot have zero length.' The error points to Extended_JCIMS_MVC2_EF_Entities.cs Line: 25
Line 25: public ExtendedJCIMS_DevEntities(string connectionString)
Line 26: :base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
I am unable to determine what is causing this error. I assume the error is referring to the connection string from the Web.Config file. It does not like the 'connectionString' variable. I'm obviously doing something worng. I would appreciate a push in the right direction.
The relevant bits are as follows:
Web.config
--------------------------------------------------------------------------
<add name="JCIMS_DevEntities"
connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=MyServer;Initial Catalog=MyDatabase;User ID=MyUser;Password=myPassWord;MultipleActiveResultSets=True""
providerName="System.Data.EntityClient"/>
<system.data>
<DbProviderFactories>
<add name="EF Tracing Data Provider" invariant="EFTracingProvider" description="Tracing Provider Wrapper"
type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
<add name="EF Generic Provider Wrapper" invariant="EFProviderWrapper" description="Generic Provider Wrapper"
type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
</DbProviderFactories>
</system.data>
Global.ascx
-----------------------------------------------------------------------
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
//EFTracingProviderConfiguration - LOG ALL Sql commands
EFTracingProviderConfiguration.LogToFile = Server.MapPath("~/JCIMS_MVC2_EF_SQL_Logfie.txt");
}
Extended_JCIMS_MVC2_EF_Entities.cs
--------------------------------------------------------------------------
namespace JCIMS_MVC2_EF.DomainModel
{
/// <summary>
/// Partial calss that Extends the EF Datacontext Class
/// </summary>
public partial class ExtendedJCIMS_DevEntities : JCIMS_DevEntities
{
private TextWriter logOutput;
public ExtendedJCIMS_DevEntities()
: this("name=JCIMS_DevEntities")
{
}
public ExtendedJCIMS_DevEntities(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString,
"EFTracingProvider"
))
{
}
//... and more
}
}
SearchRepository.cs
------------------------------------------------------------------
public class SQLSearchRepository : ISearchRepository
{
//Database connection
private ExtendedJCIMS_DevEntities db = new ExtendedJCIMS_DevEntities(); // tracing version
public IEnumerable<SearchResults> ListAll(string strSearch, string chkSearch)
{
return (from s in db.Schools....
// and more...
}
Appreciate any assistance anyone can give me...
Have you debugged and confirmed that the connectionString passed into the ExtendedJCIMS_DevEntities method is not null or empty? That's what the error seems to indicate.