Azure Function App Could not load System.IO.Pipelines connecting to Redis - stackexchange.redis

Created a new Function App, HTTP trigger in VS2019 (16.8.3) to connect to Azure Cache for Redis. Added StackExchange.Redis 2.2.4 from nuget.
local.settings.json contains the key/value of RedisConnectionFromKeyVault and the Primary Connection String from Access keys from the portal.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"RedisConnectionFromKeyVault": "<<SNIP>>.redis.cache.windows.net:6380,password=<<SNIP>>,ssl=True,abortConnect=False"
}
}
Added the following lines to the default function code:
var connectionRedis = Environment.GetEnvironmentVariable("RedisConnectionFromKeyVault", EnvironmentVariableTarget.Process);
var cache = ConnectionMultiplexer.Connect(connectionRedis).GetDatabase();
When I run and trigger the function app locally I get the following exception on the ConnectionMultiplexer.Connect call.
System.Private.CoreLib: Exception while executing function: Function1. StackExchange.Redis: Could not load file or assembly 'System.IO.Pipelines, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
at StackExchange.Redis.ConnectionMultiplexer.Connect(ConfigurationOptions configuration, TextWriter log) in /_/src/StackExchange.Redis/ConnectionMultiplexer.cs:line 1032
at StackExchange.Redis.ConnectionMultiplexer.Connect(String configuration, TextWriter log) in /_/src/StackExchange.Redis/ConnectionMultiplexer.cs:line 1015
at FunctionApp1.Function1.<Run>d__0.MoveNext() in E:\GitRepos\FunctionApp1\FunctionApp1\Function1.cs:line 26
Tried similar code in a console app and it works fine?
What am I missing? Why does the function app think it cannot find the System.IO.Pipelines assembly?
Even if I include the System.IO.Piplelines nuget package explicitly it does not find it?

Looks like this is a known issue with Azure Functions as noted at https://github.com/Azure/azure-functions-host/issues/5894
Issues were raised with StackExchange.Redis
https://github.com/StackExchange/StackExchange.Redis/issues/1637
https://github.com/StackExchange/StackExchange.Redis/issues/1655
Issue can be resolved by adding the _FunctionsSkipCleanOutput element as below to the csproj
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput> <!-- *** this line is new ** -->
</PropertyGroup>

OK I found how to workaround this.
I was referring to the package StackExchange.Redis 2.2.4 (which was making Azure wanting to load System.IO.Pipelines 5.x).
I replaced that package with Microsoft.Extensions.Caching.StackExchangeRedis 5.0.1 (which in turn references StackExchange.Redis 2.0.593 which references System.IO.Pipeline 4.5.2).
Now my functions are working correctly.
Not sure if this is a long term solution though.
Thanks.

Related

MikroORM repository throwing Global Context error when debugging in Visual Studio Code

I am trying to use MikroORM repositories, but I keep getting the Global Context validation error when I use them while debugging in Visual Studio Code. I am using the RequestContext middleware, just as the documentation and demo applications suggest. The error does not occur when I run the application in a terminal window instead of by debugging.
I am currently running Visual Studio Code version 1.68.1 on Windows 10, using Node version 18.2.0.
Error Recreation:
Install the sample application Express + MongoDB + JavaScript from the Mikro-orm Example integrations (https://github.com/mikro-orm/express-js-example-app).
Follow the instructions in the README.md to run the application.
Use Postman (or equivalent) to create an author.
3.1. POST http://localhost:3000/author\
3.2. Body (raw/JSON):
{
"name": "test person",
"email": "test#test.com",
"age": 42
}
Use Postman (or equivalent) to retrieve authors.
4.1. GET http://localhost:3000/author
Copy the id of the author you just created.
Use Postman (or equivalent) to update the author.
6.1. PUT http://localhost:3000/author/<id from step 5.>
6.2. Body (raw/JSON):
{
"name": "test 'success' person"
}
NOTE: This will succeed, proving that everything is configured correctly. I have tried this with both the repository's version of Mikro-orm as well as the current latest version (5.2.3).
Stop the application (Ctrl-C in terminal)
Attach the debugger
8.1. Open the file app\server.js
8.2. Open VS Code's Run and Debug panel.
8.3. Click the "Run and Debug" button.
Use Postman (or equivalent) to update the author.
9.1. PUT http://localhost:3000/author/<id from step 5.>
9.2. Body (raw/JSON):
{
"name": "test 'failure' person"
}
This time the update will fail with the message:
"Using global EntityManager instance methods for context specific
actions is disallowed. If you need to work with the global instance's
identity map, use allowGlobalContext configuration option or
fork() instead."
Within the sample application, the error occurs in author.controller.js, within the router.put('/:id', async (req, res) section, specifically on the line await DI.authorRepository.flush() (line 55). However, experimentation suggests that the error will occur the second time you make any repository call. For example, if you duplicate the findOneOrFail line like this:
const author = await DI.authorRepository.findOneOrFail(req.params.id);
const foo = await DI.authorRepository.findOneOrFail(req.params.id);
the error will occur on the second call to .findOneOrFail.
The error is being thrown in #mikro-orm/core/EntityManager.js, in the getContext method (line 737). When the second call to the repository occurs, getContext is called multiple times. The first time has the following call stack
EntityRepository.flush
MongoEntityRepository.em (get)
EntityManager.getContext
This call is successful.
However, the second call to getContext fails. It has this call stack:
EntityRepository.flush
EntityManager.flush
EntityManager.getUnitOfWork
EntityManager.getContext
The issue seems to be that between the two calls to getContext, em._id goes from not 1 to 1. It looks to me like the EntityManager with an _id of 1 is the global context we shouldn't be using. However, when you run the code without debugging, em._id is not 1 during any of these calls.
I have not been able to figure out why the context is not retrieving the correct entity manager when in debugging mode.

Xamarin - .NET Standard - Use Azure DevOps Services with VssAadCredential -

I want to create a Xamarin.Forms app where I have to login to a specific Azure DevOps environment/project.
To try out the Azure DevOps Service library I first created a Console App (.NET Framework 4.7.2) to login to the Azure DevOps environment/project. The following code was used for login process (+ extra code to validate the connection actualy works).
public void Login(string _userName, string _pwd)
{
ProjectHttpClient projectClient;
this.Credentials = new VssAadCredential(_userName, _pwd);
this.Connection = new VssConnection(new Uri(this.DevOpsPath), this.Credentials);
this.InitReferences(this.ProjectName);
projectClient = this.Connection.GetClient<ProjectHttpClient>();
this.ProjectReference = projectClient.GetProjects(null, top: 1).Result.Where(item => item.Name == this.ProjectName).FirstOrDefault();
}
When I use the same piece of code in the Xamarin.Forms App (.NET Standard 2.1) it no longer works and I get the following error when executing the last line:
One or more errors occurred. (Could not resolve type with token
0100008d from typeref (expected class
'Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions'
in assembly 'Microsoft.IdentityModel.Clients.ActiveDirectory,
Version=3.19.4.11002, Culture=neutral,
PublicKeyToken=31bf3856ad364e35'))
When using the VssBasicCredential with a personal acces token, the code runs as expected. However I would prefer using the VssAadCredential and not the VssBasicCredential.
I'm not aware that the VssAadCredential is not supported in .NET Standard and can find no documentation relating to the issue.
Has anyone had a similar experience that might solve this problem or can anyone provide me with some documentation declaring that this cannot work as of yet?

uirouter/angular-hybrid AoT build bootstrapModuleFactory promise injector fails to get UIRouter

I've got a sample uirouter/angular-hybrid app, successfully built with #ngtools/webpack AngularCompiler plugin and running. I've updated the main.aot.ts boot function to use bootstrapModuleFactory and can get the injector from the platformRef available in the promise success handler. But injector.get(UIRouter) fails with "Cannot read property 'config' of null."
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory).then((platformRef) => {
const urlService: UrlService = platformRef.injector.get(UIRouter).urlService;
function startUIRouter() {
urlService.listen();
urlService.sync();
}
platformRef.injector.get<NgZone>(NgZone).run(startUIRouter);
});
I confirmed that the injector.get(NgZone) will succeed and injector.get(UIRouter) will fail. I tried moving the call to injector.get(UIRouter) inside the NgZone run func without success.
I also tried moving the upgrade.bootstrap call into the promise success function above to ensure it had booted first, without fixing the problem.
A simple angularjs component is rendering fine, so the boot process seems to be succeeding, except for not being able to call the listen() and sync() functions on the UIRouter.urlService.
I also confirmed the development config and non-aot production config, for this same sample app, do not have this problem and seem to be working fine.
Using versions:
uirouter/angular-hybrid v6.0.2
angular packages at v7.1.4, but also failed with 6.0.0 (which is the angular version in the package.json in the docs for uirouter/angular-hybrid v6.0.2)
Thanks for any ideas.
The problem was that the config object I was passing to UIRouterUpgradeModule.forRoot was being imported from a file that was using a default export of the object, and the object had a reference to a config function that was not being exported. This combination hid the problem during the build, and resulted in the symptom at runtime of not having the UIRouter object available to the injector.
Replacing the default export with a named export triggered the AOT compiler to complain about the non-exported function reference. Additionally exporting the function then allowed a successful build, a happy injector, and a successful runtime boot.

Azure Functions, Entity Framework and Oracle DB - basic POC fails

I'm having a lot of trouble getting a basic proof-of-concept working, in which I am accessing an Oracle DB (11g) through Azure Functions via Entity Framework (6.2).
Prerequisites:
ODT For Visual Studio 2017 is installed, as well as Azure Functions CLI/Core Tools. Everything mentioned below is done entirely via Visual Studio 2017, not through Azure portal.
Take 1:
Created a new project with the Azure Functions template.
Installed NuGet packages EntityFramework (6.2.0), Oracle.ManagedDataAccess (12.2.1100) and Oracle.ManagedDataAccess.EntityFramework (12.2.1100). Note: When installing NuGet packages in projects using the Azure Functions template, the packages are added under Dependencies -> NuGet, rather than under References.
Added ADO.NET Entity Data Model to project.
Problem: After setting my connection string, choosing Entity Framework 6.x is unavailable, with the following error message:
An Entity Framework database provider compatible with the latest
version of Entity Framework could not be found for your data
connection. If you have already installed a compatible provider,
ensure you have rebuilt your project before performing this action.
Otherwise, exit this wizard, install a comaptible provider, and
rebuild your project befre performing this action.
As the simplest of workarounds, I have tried to just go ahead with EF5, but it throws an exception while creating the DB model (after selecting the objects to include in model, including some stored procedures).
Take 2:
Created project and installed NuGet packages as above.
Created class library project to facilitate the Oracle interactions.
Installed the same NuGet packages as above in the class library project.
Added ADO.NET Entity Data Model to class library project and added some database objects to the database model. Also added custom constructor to the model for specific connection string, because managing connection strings in Azure Functions was a seperate set of headaches that I'll deal with later.
Added a simple wrapper method to the class library project that calls a stored procedure from the database model:
public static string NameByEmpNo(int empNo)
{
string result;
MyEntities entities = new MyEntities("metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|res://*/MyEntities.msl;provider=Oracle.ManagedDataAccess.Client;provider connection string='DATA SOURCE=127.0.0.1:1521/ORCL;PASSWORD=tiger;USER ID=SCOTT'");
ObjectParameter name = new ObjectParameter("o_empname", typeof(string));
entities.GET_EMP_NAME_PROC(empNo, name);
result = (string)name.Value;
return result;
}
Added reference to the class library in the Azure Functions project.
Added function that calls NameByEmpNo:
[FunctionName("GetNameByEmpNo")]
public static async Task<HttpResponseMessage> GetNameByEmpNo([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequestMessage req, TraceWriter log)
{
int empNo = Int32.Parse(req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "empno", true) == 0)
.Value);
string empName = ScottAccess.NameByEmpNo(empNo);
return req.CreateResponse(HttpStatusCode.OK, "Employee name: " + empName);
}
Problem: At runtime, calling the function fails with this error
message:
Exception while executing function: GetNameByEmpNo -> The ADO.NET
provider with invariant name 'Oracle.ManagedDataAccess.Client' is
either not registered in the machine or application config file, or
could not be loaded. See the inner exception for details. -> Unable to
find the requested .Net Framework Data Provider. It may not be
installed.
Bonus info: My class library works perfectly when called through a console application. Also, my Azure Functions app works perfectly when calling functions that do not use my class library...
I am stumped. Has anyone got experience with getting this combination of techs working together and can offer some insight into where I'm going wrong / provide steps to get a basic connection working?
Entity Framework within Azure Functions defaults the providers to System.Data.SqlClient so SQL connections will work without any configuration changes, but that means you have to do something special for Oracle connections. The problem seems to come from the config values that the Oracle.ManagedDataAccess.Client library assumes are available within the App.Config or Web.Config file in the project, which are inserted whenever you install the Oracle.ManagedDataAcess.EntityFramework Nuget package. Azure Functions don't have config files, and I wasn't able to find any way to specify the Oracle provider in the settings json files.
I found a solution in this post
It suggests bypassing this mechanism and creating a DbConfiguration for Oracle, then using DbConfigurationType to tell the DbContext which configuration you're using.
public class OracleDbConfiguration : DbConfiguration
{
public OracleDbConfiguration()
{
SetDefaultConnectionFactory(new OracleConnectionFactory());
SetProviderServices("Oracle.ManagedDataAccess.Client", EFOracleProviderServices.Instance);
SetProviderFactory("Oracle.ManagedDataAccess.Client", new OracleClientFactory());
}
}
[DbConfigurationType(typeof(OracleDbConfiguration))]
public partial class MyEntities : IGISContext
{
//Expose Connection String Constructor
public MyEntities(string connectionString, int commandTimeoutInSeconds = 30) : base(connectionString)
{
this.Database.CommandTimeout = commandTimeoutInSeconds;
}
}
Note: I used EF 6 Database First to generate my EDMX; MyEntities here is a partial class for providing a constructor that takes in a connection string.
The oracle connection wil use the specified DbConfiguration class, and any SQL database connections will continue to work using the defaults.
My solution is using the Nuget Packages:
EntityFramework 6.2.0
Oracle.ManagedDataAccess 12.2.1100
Oracle.ManagedDataAccess.EntityFramework 12.2.1100

Why are no embedded files found in EmbeddedFileProvider in asp.net core mvc?

I'm currently trying to load embedded ViewComponents from external assemblies.
I've included this in my project file:
<EmbeddedResource Include="Views\**\*.cshtml" />
so when I inspect the actual assembly and run GetManifestResourceNames() I see that the file is embedded.
I'm then calling this method in ConfigureService() in Startup.cs:
public static IMvcBuilder GetModules(this IMvcBuilder mvcBuilder)
{
var embeddedFileProviders = new List<EmbeddedFileProvider>
{
new EmbeddedFileProvider(Assembly.GetCallingAssembly())
};
mvcBuilder.ConfigureApplicationPartManager(apm =>
{
foreach (string modulePath in Directory.GetFiles(Configuration.Settings.Path, "*.Module.dll"))
{
var assembly = Assembly.LoadFrom(modulePath);
var startUpType = (from t in assembly.GetTypes()
where t.GetInterfaces().Contains(typeof(IModuleStartup))
select t).FirstOrDefault();
RegisterModuleServices(mvcBuilder, startUpType);
apm.ApplicationParts.Add(new AssemblyPart(assembly));
embeddedFileProviders.Add(new EmbeddedFileProvider(assembly));
Modules.Assemblies.Add(assembly);
}
var compositeFileProvider = new CompositeFileProvider(embeddedFileProviders);
mvcBuilder.Services.AddSingleton<IFileProvider>(compositeFileProvider);
});
return mvcBuilder;
}
I'm also not using
mvcBuilder.Services.Configure<RazorViewEngineOptions>(o =>
{
o.FileProviders.Add(compositeFileProvider);
});
as this doesn't work at all and the action o.FileProviders.Add(compositeFileProvider) is not even called.
All the embedded file providers are found when I inject IFileProvider but none of the files are found when I run _fileProvider.GetDirectoryContents("");
Does anybody have any idea why?
So i figured out why it wasn't returning anything...
It seems that I didn't set the baseNameSpace parameter when created the new EmbeddedFileProvider. stupid huh.
But there were quite a few examples that didn't set this and it worked.
Hopefully this helps some other people out there if they experience this issue.
Watch also your project root namespace setting. My case was the reverse - I copy-n-pasted a project file and it did not retain the namespace setting from the previous project. This was because I did not explicitly set <RootNamespace>YourNameSpaceNameHere</RootNamespace> in the .csproj settings
(nested under the <PropertyGroup> block at the top), so it took my file name as the namespace! It was quite a "gotcha" moment, and much time lost, to find out my code correctly sets the baseNameSpace parameter, but the whole time the project was storing the files under a different namespace! (you can open the DLL in any text editor, scroll to the bottom, and you should easily be able to make out the embedded text to verify). It was there, just not found. In case someone has this correct, you can also dump ALL files using {Assembly}.GetManifestResourceNames() and make sure your names are correct.
In my case I had '.' (period) in the resource filename.
I had this error in an ASPNET Core 3.0 project, where my external class library had the file correctly embedded, but the web application was not locating them at runtime. It turns out the example I copied from the internet had a namespace provided and I copied that example namespace without considering the implications...
After a bit of research, I was able to fix it by simply using the proper root namespace defined my own Class Library:
var embeddedFileProvider =
new Microsoft.Extensions.FileProviders
.EmbeddedFileProvider(assembly, "ViewComponentLibrary");
changed to
var embeddedFileProvider =
new Microsoft.Extensions.FileProviders
.EmbeddedFileProvider(assembly, "MyProjectLibrary");
We had another root cause leading to this problem. We had migrate our build agents from windows to linux, and FS case-sensitivity of the latter did the trick - it didn't found embedded resources:
<EmbeddedResource Include="swagger\ui\*" />
because on file system we have Swagger\ui\
So the #(EmbeddedResource) path must be the same as the File System path:
<EmbeddedResource Include="Swagger\ui\*" />
(or rename files/directories, to match the #(EmbeddedResource).

Resources