Azure Functions: Can compile but cannot run with custom datalayer library - entity-framework

I've tried to come up with a better title but can't.
The issue is I am new to Azure functions but have made a simple one work that writes to a SQL Azure table. Now I've attempted to build the simplest kind of Entity Framework based Datalayer and uploaded it. Right now it is compiled as .Net 4.6 and using EF 6.1.3.
I'm using a connection string as per the second answer here Second answer and have checked it is being retrieved correctly. Update - I also used this guide.
Removing this {#r "D:\home\site\wwwroot\sharedbin\TestDataLayer.dll"} causes the editor to complain about missing assemblies, so it IS finding the dll in question.
However it will not run - it cannot find TestDataLayer.dll.
I'm only running this in the portal editor (I've not yet mastered deployment direct from a Visual Studio Project - don't laugh :P).
#r "System.Configuration"
#r "System.Data.Entity"
#r "D:\home\site\wwwroot\sharedbin\TestDataLayer.dll"
using System;
using System.Collections;
using System.Configuration;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Data.Entity.SqlServer;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Net;
using TestDataLayer;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
var connection = ConfigurationManager.ConnectionStrings["sql_connection"].ConnectionString;
using(var db = new SyncDbContext(connection))
{
var RK = new RKAzureTest() {TestField1 = "It finally worked?" };
db.RKAzureTests.Add(RK);
db.SaveChanges();
}
}
[DbConfigurationType(typeof(myDBContextConfig))]
public partial class SyncDbContext : System.Data.Entity.DbContext
{
public SyncDbContext(string cs) : base(cs) {}
public DbSet<RKAzureTest> RKAzureTests {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class myDBContextConfig : DbConfiguration
{
public myDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
System.Data.Entity.SqlServer.SqlProviderServices.Instance);
SetDefaultConnectionFactory(new System.Data.Entity.Infrastructure.SqlConnectionFactory());
}
}
This is the function.json:
{
"frameworks": {
"net46":{
"dependencies": {
"EntityFramework": "6.1.3"
}
}
}
}
I've compiled the dll itself to .Net 4.6 after a suspicion that the Azure Functions don't support .net 4.7.1 and via Kudu uploaded the compiled dll to a sharedbin folder (checked the path a dozen times!).
This is the error thrown up:
2018-05-01T11:00:00.012 [Warning] Unable to find assembly 'TestDataLayer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Are you missing a private assembly file?
2018-05-01T11:00:00.012 [Error] Exception while executing function: Functions.TimerTriggerCSharp1. mscorlib: Exception has been thrown by the target of an invocation. f-TimerTriggerCSharp1__514732255: Could not load file or assembly 'TestDataLayer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Not quite sure what else can be left - I'm using runtime version 1.0.11702 in the Application settings as I found life got a LOT more complicated if I went onto the Beta version.
If anyone can point me to a working guide for this use case (Database first, EF 6.1.3 etc) I'd be grateful.
Any help offered gratefully received!
Thank you :)

Go to Azure Portal, create a folder called, 'bin' inside your Azure functions using CMD Shell, upload the 'TestDataLayer.dll' file to bin folder which has just been created.
#r "System.Configuration"
#r "System.Data.Entity"
#r "TestDataLayer.dll"
Project structure should look like,
AzureFunctionProjectName001
bin
TestDataLayer.dll
run.csx
project.json
project.lock.json
...
Azure functions should be able to discover your library this time. I believe, EntityFramework works just fine.

Related

EF Core tools System.Configuration.ConfigurationManager assembly not found

I am creating a new application that is using EF Core 2 with migrations.
The application itself is .net Framework but the model is in a separate .net core 2.0 assembly. Everything is working fine I have defined a designtime context factory:
public class MyDesignTimeContextFactory : IDesignTimeDbContextFactory<MyContext>
{
public MyContext CreateDbContext(string[] args)
{
return new MyContext("Server=localhost\\SQLEXPRESS;Database=DBName;User ID=Test;Password=0000;");
}
}
And I can generate migrations and apply/revert them to the DB.
Now if I replace hardcoded connection string with a call to config file
return new MyContext(System.Configuration.ConfigurationManager.AppSettings.Get("ConnectionString");
I have an error when calling EF Core tools:
Add-Migration -Project MyProject.Model -Name Initialization
System.IO.FileNotFoundException: Could not load file or assembly 'System.Configuration.ConfigurationManager, Version=4.0.1.0 ....,
However the nuget is there and I can access ConfigurationManager in ContextFactory (not the designtime one) with no problem when launching the application itself. Looks like EF core tools are not looking for the dependencies of the model assembly...
Is there something I am missing? Maybe it is not possible to use ConfigurationManager in DesignTime context factory?
Finally the problem was in the application project. I had to add the nuget package for System.Configuration.ConfigurationManager to the .Net Framework app so the PackatManager can find it. A bit weired that it works at runtime but not in "design mode".

Entity Framework Core 2 context and model in separate project to main project

How do i add-migrations into a separate data project??
I've got two projects, a data project with the context and models in and the website project which will use it. I've linked the two together all fine and dandy, the problem is that when I try and add a migration to the data project with add-migration initialmigration, the error
Unable to create an object of type 'myContext'. Add an implementation of 'IDesignTimeDbContextFactory' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.
The link has details on how to add the IDesignTimeDbContextFactory but which doesn't work because UseSqlServer doesn't exist as a command. That page does link to the updated version at https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#update-main-method-in-programcs which says to put code into program.cs Even if I follow the instructions and add it to my data project (along with the associated references) so that I include
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
which is what it says to do, but I get the same error message. So, regardless of whether I include the section of code or not I get the same error message, despite the Microsoft.com page stating that the above code stops the error message I'm getting from appearing.
I've tried putting the context and model into my main project and that generates migrations fine, so the code for those is fine at least.
How do i add-migrations into a separate data project??
Add-Migration has a -Project parameter.
Add-Migration -Project MyProject -Context MyDbContext -Name InitialMigration
EDIT: To see all options for a cmdlet (since Package Manager Console is just PowerShell) do
Get-Help Add-Migration -Full
Your DbContext in class library should look like this:
public class ExampleDbContext : DbContext
{
public UserProfileDbContext(DbContextOptions<ExampleDbContext > options)
: base(options) { }
public DbSet<Example> Examples{ get; set; }
}
And in your Startup you should add:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ExampleDbContext >(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
}
This is when you are using MS Sql server and have connection string in appsettings.json
Then for example use package manager console for your class library project with EF and it should work.
You should have following nuget packages in you class library project:
Microsoft.EntityFrameworkCore,
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools

How do I load Nuget packages that a custom DLL depends on?

I'm developing a Function App via the Portal (not local development). I have a custom DLL that depends on 1 nuget package: Entity Framework 6.1.3
I have uploaded my DLL to "../bin" and my code compiles successfully when I reference my DbContext object. So far, so good.
I also have a Project.json file and I see it acquiring the nuget packages when I save. So far, so good.
{
"frameworks": {
"net46":{
"dependencies": {
"EntityFramework": "6.1.3"
}
}
}
}
My Run.csx code compiles successfully and looks like this:
#r "../bin/Library.dll"
using System;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
log.Info("My code: Started");
log.Info(typeof(Library.MyContext).ToString());
}
However, the code doesn't actually run (I don't even see the "My code: Started" log item). The error I receive is:
2017-02-27T06:37:28.731 Exception while executing function:
Functions.TimerTriggerCSharp1. mscorlib: Exception has been thrown by
the target of an invocation. f-TimerTriggerCSharp1__-205940111: Could
not load file or assembly 'EntityFramework, Version=6.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its
dependencies. The system cannot find the file specified.
My custom DLL is the simplest possible EF-referencing DLL I can possibly make. All you need to recreate it is this:
Custom DLL packages.config:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="6.1.3" targetFramework="net46" />
</packages>
Custom DLL Class1.cs
using System.Data.Entity;
namespace Library
{
public class MyContext : DbContext
{
}
}
What am I doing wrong that is keeping my custom DLL from being able to utilize the downloaded EntityFramework nuget package?
Just to verify that my nuget references are actually working, if I comment out most of my Run.csx code and replace it with this line, all executes correctly and logs what you would expect:
log.Info($"My code: {typeof(System.Data.Entity.DbContext).ToString()}");
As somebody suggested, I have tried changing my Run.csx references to look like this and it doesn't change the runtime error I get (it does compile so the path is correct):
#r "../../../data/Functions/packages/nuget/entityframework/6.1.3/lib/net45/EntityFramework.dll"
#r "../bin/My.dll"
I can also change my Run.csx file to contain this code and it does successfully execute:
using System;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
log.Info(typeof(MyContext).ToString());
}
public class MyContext : System.Data.Entity.DbContext
{
}
In order to consume the assemblies coming from your referenced packages, you can deploy your custom dependency as a private assembly:
Deploy the assembly into a bin folder, in your function folder (e.g. wwwroot\myfunction\bin)
Reference the assembly without the relative path, by file name only (e.g. Library.dll)
If you wish to use shared assemblies, deployed to a common location and referenced as you have above, you'd need to deploy the assembly with its dependencies (essentially the output from your project build).
Another option that I would recommend if you want to take advantage of the shared model is to deploy your dependency as a NuGet package (which you can deploy to either a custom source hosted somewhere or as a file), that package would then specify its package dependencies and they would all be resolved automatically.
Looks like configuration problem, you need to define the reference in the project file to be copied to output.
In the Solution Explorer, expand the project, References, right click the EntityFramework, Properties, Set Copy Local = true.
Sometimes the default value is false, so then it will not in the output folder of the project.
I hope this helps.

Nuget packages with common Names

I was playing with NuGet so I created a project, wrote two simple functions (multiply and add), packaged it with the following spec:
<metadata>
<id>Math</id>
<version>1.0.0.0</version>
<title>$title$</title>
<authors>$author$</authors>
<owners>$author$</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<copyright>Copyright 2015</copyright>
<tags>Matematica, Test</tags>
</metadata>
Then publicized it on my local NuGet.Server feed, added my new nuget package in another sample project, started it and it returns this:
Could not load file or assembly 'Math, Version=1.0.0.0, Culture=en-GB, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Googled for various time but found nothing (was I the only dumb enough to call a temporary package 'Math'?), so I imagined that somewhat my module name was taboo, changed it into Math_Test, same source code and it worked like a charm.
Is there some more informations out there that specifies which values are taboo?
I can't find anything on nuget official documentation.
The class Math is specified in the following dll (mscorlib.dll) so it doesn't even have the same id...
#region Assembly mscorlib.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\mscorlib.dll
#endregion
EDIT:
And here is the code of the sample application
using Math;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int a = -1;
try
{
Common e = new Math.Common();
a = e.Multiply(3, 2);
}
catch (Exception e)
{
System.Console.Out.Write(e);
}
System.Console.Out.Write(a);
System.Console.Read();
}
}
}
Seems like the problem was not the name but the Culture inside the Assembly (which is not the same as the language specified into Nuget spec.
Changing the AssemblyCulture from
[assembly: AssemblyCulture("en-GB")]
to
[assembly: AssemblyCulture("")]
solved the issue.
I found the warning by toggling diagnostic output during build, as suggested by #ShyamalDesai
CSC : warning CS1607: Assembly generation -- Referenced assembly 'Math' has different culture setting of 'en-GB'

Building and loading file in NUnit

I am following a book called "The Art of Unit Testing". I have reached a point where I need to test my test method that I have writtent using NUNit. The author instructs to build the project and then locate the path to the assembly file that was built and give the path to NUnit for testing.
My problem is that I cant seem to get this Assembly file path. Where is it located?
Plus, when I run my code, I am getting the following error:
Error 2 Program 'c:\Users\Documents\Visual Studio 2012\Projects\Loganalyzer\Loganalyzer\obj\Debug\Loganalyzer.exe' does not contain a static 'Main' method suitable for an entry point c:\users\documents\visual studio 2012\Projects\Loganalyzer\Loganalyzer\CSC Loganalyzer
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Loganalyzer
{
public class LogAnalyzer
{
public bool IsValidLogFileName(string fileName)
{
if (!fileName.EndsWith(".SLF"))
{
return false;
}
return true;
}
}
}
I am following the exact example that's in the book but cant get it to work as you can see. I will appreciate your help folks.
using Loganalyzer;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogAnalyzerTest
{
[TestFixture]
class LogAnalyzerTest
{
[Test]
public void IsValidFileName_validFile_ReturnsTrue()
{
//Arrange ( Arranges objects, creating and setting them up as necessary).
LogAnalyzer analyzer = new LogAnalyzer();
//Act
bool result = analyzer.IsValidLogFileName("whatever.SLF");
//Assert ( Asserts that something is as expected)
Assert.IsTrue(result, "file name should be valid");
}
}
}
It seems as though you're trying to run your class project Loganalyzer, but you'll probably be wanting to use some sort of test runner. I prefer TestDriven.net.
NUnit is just the testing framework (very simplified, it specifies the rules for how to set up tests, etc).
What you need is some application or plugin to actually run them. You run the tests in the concole runner, or the GUI runner that come with NUnit for example, or in TestDriven.net (which I've heard is excellent).
Personally, I use the runner that comes with Resharper (although that is only free to try for a month or so).
The point is that you don't have an executable project, but rather a class library, containing stuff to test. The runner runs your tests, which in turn, call your code.
If you want to use the native nunit runner you typically use a class like this
static class NUnitLauncher
{
[STAThread]
static void Main()
{
AppEntry.Main(new[] { Assembly.GetExecutingAssembly().Location });
}
}
You'll also have to have it set as the start-up object (in the project's properties).
You'll also need to reference nunit-gui-runner.dll which you can find in the nunit install directory (normally program files). Mine is at
C:\Program Files (x86)\NUnit 2.5.7\bin\net-2.0\lib
The native nunit runner has it's problems but I find the resharper (6.1) test runner is unstable when debugging - it sometimes bombs out randomly. It also doesnt understand all the different types of parameterised tests you can have in nunit. Hopefully this isnt the case in the newer versions - it's got a much nicer UI.
The problems you describe above are two seperate things - one sounds like it is because you dont have a startup object set; the other sounds like it is confusion about what an assembly is. The executable that gets produced when you compile will contain an assembly - if you point nunit at that then it should work (assuming it has some nunit stuff in there (eg stuff tagged up with [Test] etc))