I am trying to get an Azure function to work with EF6 and having some weird sporadic problems. It has worked twice, but has not worked the other hundred times. The error I get is:
Microsoft.Azure.WebJobs.Script: One or more errors occurred. EntityFramework: An instance of 'MyDbConfiguration' was set but this type was not discovered in the same assembly as the 'MyDbContext' context. Either put the DbConfiguration type in the same assembly as the DbContext type, use DbConfigurationTypeAttribute on the DbContext type to specify the DbConfiguration type, or set the DbConfiguration type in the config file. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information.
I am programmatically setting the provider because I don't know any other way to do it without an app.config file. I've ran into the (terrible) DbConfiguration singleton limitation of EF before, but in this case I don't understand why it's not working.
Is the Azure functions runtime splitting my .csx file up into multiple assemblies? I've tried changing things to nested/private classes, internal, and various other random changes but it just doesn't work (or isn't consistent at the very least). My next attempt will be uploading an App.config file as normal and seeing if that works...
The disconcerting thing is that sometimes it does work. It seems random, and rare, however. Possibly after sitting idle for a while; I tried some more changes while writing this, and it worked initially, then didn't, then still didn't work after reverting my changes.
I am editing and testing it via the Azure Portal Save and Test buttons (and writing the code first in VS for syntax checking and IntelliSense...). Here is the code (updates will follow):
using System.Net;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");
using (var db = new MyDbContext("...")) {
log.Info(db.Events.First().id.ToString());
}
}
#region POCOs
public class Event {
public long id {get;set;}
public string data {get;set;}
}
#endregion
[DbConfigurationTypeAttribute(typeof(MyDbConfiguration))]
public class MyDbContext : System.Data.Entity.DbContext {
public DbSet<Event> Events {get;set;}
public MyDbContext(string cs) : base(cs) { }
}
public class MyDbConfiguration : System.Data.Entity.DbConfiguration {
public MyDbConfiguration() {
SetDefaultConnectionFactory(new System.Data.Entity.Infrastructure.SqlConnectionFactory());
SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
}
}
Update 1: Even worse, if I change the name of a class (e.g. DbConfigurationx), saving and running it gives the same error as above, for the old class name, so I'm not confident that the code in the editor is the code being run...which is a bit worrisome. Is this how the service is supposed to work?
Update 2: Adding the App.config didn't make a difference. Nuts.
Update 3: Adding a Web.config seemed to fix it--it worked for half a dozen tests, but then stopped working again. I also saw log messages appear out of order, it was running and failing the test sometimes before compilation finished, so I'm really not sure WTF is going on with this service and I'm not sure I can recommend using it anywhere if it's this inconsistent. I was able to get it working by just avoiding all of the EF startup crap and creating my own database connection and passing that into the context. If this actually proves reliable over time I will add an answer with how I did it. I suppose manually opening the DB connection is okay since this is a one-off request, unfortunately I think that means it doesn't use a shared connection pool so it will slow everything down (it certainly seems to so far).
Assuming this will work, overall I'm pretty disappointed; AWS Lambda is a much bigger PITA to get going, but it is predictable, the latter being much more important. The time it took to get things working was about the same for me (dealing with EF and general weirdness for Azure, console config for AWS).
Update 4: Well I tried moving the POCOs into another assembly, which I planned on doing anyway once I got things working (tried being the operative word). I changed to using Git to deploy so I could build in VS. First I got the single-file version working via Git deployment, which took a few hours. Building another DLL means I need to add Newtonsoft.Json (Json), for JsonIgnore, and EF to it. I ran into Json version problems (DLL hell is alive and well in .NET), but that stuff isn't documented anywhere that I could find, so I added Json as a nuget package along side EF in my project.json file but then it stopped being able to update:
2016-05-22T08:40:36 Updating branch 'master'. 2016-05-22T08:40:37
Could not remove 'D:/home/site/wwwroot/execution/bin/common.dll': Access is denied.2016-05-22T08:40:39 Error occurred, type: error, text: Could not remove 'D:/home/site/wwwroot/execution/bin/common.dll': Access is denied. , stackTrace: at LibGit2Sharp.Core.Ensure.HandleError(Int32 result) at LibGit2Sharp.Core.Proxy.git_checkout_tree(RepositorySafeHandle repo, ObjectId treeId, GitCheckoutOpts& opts) at LibGit2Sharp.Repository.CheckoutTree(Tree tree, IList`1 paths, IConvertableToGitCheckoutOpts opts) at LibGit2Sharp.Repository.Checkout(Tree tree, CheckoutOptions checkoutOptions, String headTarget, String refLogHeadSpec, Signature signature) at LibGit2Sharp.Repository.Checkout(Branch branch, CheckoutOptions options, Signature signature) at LibGit2Sharp.Repository.Checkout(String committishOrBranchSpec, CheckoutOptions options, Signature signature) at Kudu.Core.SourceControl.Git.LibGit2SharpRepository.Update(String id)
at Kudu.Core.Deployment.DeploymentManager.d__25.MoveNext() 2016-05-22T08:40:39 Error occurred, type: error, text: One or more errors occurred., stackTrace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at Kudu.Console.Program.PerformDeploy(String appRoot, String wapTargets, String deployer, String lockPath, IEnvironment env, IDeploymentSettingsManager settingsManager, TraceLevel level, ITracer tracer, ITraceFactory traceFactory, IOperationLock deploymentLock), innerText: Deployment failed, innerStackTrace: at Kudu.Core.Deployment.DeploymentManager.d__25.MoveNext() 2016-05-22T08:41:25.266 Job host stopped
Thinking that I broke it, I removed the Json package from project.json and deployed, which seemed to work, but now I get a 404 when I test it. Maybe I borked the whole host...restarting the App Service seemed to fix it (a running theme), but didn't. Now when I push to Git it doesn't actually update the app...the "log streaming" doesn't say anything either. Sigh.
Turns out it's the same "Access is denied" error as above, but that stuff doesn't get displayed in the Azure Functions log stream, but it does in the /api/logstream endpoint.
I guess I will [try to] go back to a single file for now.
Update 5: Well I was able to put the EF bits into a common.csx file and #load that from my run.csx. It works, technically, however I've run into another problem. While all of my code works, without modification, it's sporadic. The majority of the time (>75%) I seem to get a "bad deployment" where some of my functions don't compile and don't work. The symptom is that the system says it can't find System.Data.Entity or saying there is no System.Data.SqlClient provider. This can happen immediately when deploying, or after some requests have worked successfully. I think it's after a period of time, like when it's idle for X minutes, or some sort of internal recycle, but I'm not sure. It's as if the EntityFramework package I have specified in project.json just "disappears".
I can "redeploy" by touching (e.g. adding a blank line at the end) of the project.json file (no JSON or .csx changes), pushing through Git, and the system notices the change, attempts to download packages (but doesn't install any), and everything magically starts working. No code or package changes! Even stranger, sometimes one of my two functions breaks like this, but the other doesn't, but they get fixed the same way. I often have to re-re-re-deploy in order to get both my functions into a working state.
This article has perhaps gotten somewhat off-topic through troubleshooting. The original problem was that my EF DbContext didn't work the "normal" way using a DbConfiguration attribute because there was already a DbConfiguration in play somewhere, according to the system. I'm going to stop updating unless there are specific questions to minimize the spam.
I've found about a dozen other problems that range from annoyances or unexpected behavior to blocking issues I had to work around, but this isn't the venue to go through those. I would be happy to help repro or discuss them, but I don't know what that venue is, unfortunately.