We're having a weird problem when using Castle.Windsor to instantiate an SqlConnection using a typed factory:
The registration looks like this:
container.Register(Component.For<IDbConnectionFactory>().AsFactory().LifestyleTransient());
container.Register(Component.For<IDbConnection>().ImplementedBy<SqlConnection>()
.LifestyleTransient()
.DependsOn(Dependency.OnValue<string>
(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)));
And the IDbConnectionFactory:
public interface IDbConnectionFactory
{
IDbConnection Create();
void Release();
}
Now, when I try to access a new connection using this code:
using (var connection = _connectionFactory.Create())
{
}
I get an exception:
An unhandled exception of type
'Castle.MicroKernel.ComponentActivator.ComponentActivatorException' occurred
in Castle.Windsor.dll
Additional information: Error setting property SqlConnection.AccessToken in component
System.Data.SqlClient.SqlConnection. See inner exception for more information.
If you don't want Windsor to set this property you can do it by either decorating it
with DoNotWireAttribute or via registration API.
Alternatively consider making the setter non-public.
The problem with this Exception is that the type SqlConnection in System.Data for .NET 4.5.1 does not contain the property AccessToken whereas the one in .NET 4.6 does. In other words, if I try to manually do
var connection = new SqlConnection("connectionstring");
connection.AccessToken = "";
I get a build-error if the project is configured for .NET 4.5.1, but a runtime error on setting the AccessToken if it's configured for .NET 4.6.
Any idea why Castle.Windsor attempts to create a v4.6 SqlConnection instead of a .NET 4.5.1?
Workaround/Hack
I can get around the problem by telling Castle to ignore the property, but this seems like a hack. Doing this requires me to add it to the PropertiesIgnore in the registration:
container.Register(Component.For<IDbConnection>().ImplementedBy<SqlConnection>()
.PropertiesIgnore(info => info.Name.Equals("AccessToken"))
.LifestyleTransient()
.DependsOn(Dependency.OnValue<string>
(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)));
All .NET versions since 4.5 are in place updates
as you can see here.
This means that once you have installed .NET 4.6 you will always get the .NET 4.6 version of SqlConnection regardless of how you instantiate it.
When building your application in Visual Studio you build against a specific version of the .NET framework typically located in a folder under:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework
This means that when building msbuild can check that you are not using something which isn't available in the framework version you are targeting.
However when you run your 64 bit application it will use the assemblies typically located in C:\Windows\Microsoft.NET\Framework64\v4.0.30319
This is the same folder for all versions from .NET 4.0 through .NET 4.6, this is what in place upgrade means.
So when you execute your application on your developement environment that has .NET 4.6 installed, you will always get the .NET 4.6 version (at least unless you do something special to load other versions of the assemblies).
Castle Windsor will try to set properties with public setter and it will use reflection to find the properties which means that it will find the .NET 4.6 properties on a .NET 4.6 machine, even if you are building against 4.5.1.
The reason it fails when it tries to set the AccessToken is most likely because your connection string is not compatible with setting AccessToken.
If you check the source code of the AccessToken setter you will see that it will throw an exception if you try to set it for a incompatible connection string, even if you only try to set the AccessToken to the empty string.
As you don't need to inject any dependencies into the SqlConnection object you may as well create it simply using the new operator and then you avoid the problem caused by Windsors attempts to inject the properties of the connection.
Using this registration should work:
container.Register(Component.For<IDbConnection>().ImplementedBy<SqlConnection>()
.LifestyleTransient()
.UsingFactoryMethod(() => new SqlConnection
(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)));
Related
We have a software product that is currently released that is a .NET Framework 4.7.2 application (the "legacy" app). The legacy client-server implementation is built on System.Runtime.Remoting, which is not supported in .NET 5 and later, so the .NET 5 implementation is gRPC.
It is necessary to instantiate each of the two COM servers in turn because the legacy and the .NET 5 COM servers can only connect to the comm (not COM) server application that implements the same communications framework, which are System.Runtime.Remoting and gRPC, respectively.
The COM servers are used by third party applications to interface with the comm server application, so I am currently working on creating a static class that returns the interface from the COM server that can connect to the currently running instance of the comm server.
I have a .NET 5 WPF implementation of the product almost complete, but I've hit a roadblock in that, I am unable to register the .NET COM server.
I found these two articles:
Exposing .NET Core Components to COM
GitHub Issue
I have now been able to:
Create a Type Library
I found a comment from #SimonMourier suggesting copying the .NET 5 COM server code into a .NET Framework project and use RegAsm to export the type library to be used in the .NET 5 project. The type library was added to the .NET 5 COM server project folder and "" was added to an ItemGroup in the .csproj file per the first referenced article.
Register the .NET 5 COM server
This required using the "dotnet publish -r win-x64 -c Debug" command in the project folder from the Visual Studio Developer Command Line. I was then able to use regsvr32 to register the WinCalRemoting.comhost.dll in the "bin\Debug\net5.0\win-x64\publish" project directory.
Create an Instance of the COM Class
After registering the COM server, I am now able to create an instance of the COM class, but haven't been successful at getting the interface from it:
public static IWinCalClient LoadCompatibleRemotingClient(bool useClientEventWindow, string serverName, int serverPort, bool connectToServer = true)
{
UseClientEventWindow = useClientEventWindow;
WinCalServerName = serverName;
WinCalServerPort = serverPort;
ClassIdList = new Guid[]
{
LegacyWinCalClientClsId, // CAN'T GET INTERFACE FROM THIS COM SERVER
//WinCal5ClientClsId // THE .NET 5 COM SERVER WORKS
};
if (RemotingClassObject != null)
{
UnloadClient();
}
foreach (Guid clsId in ClassIdList)
{
try
{
RemotingClassObject = Activator.CreateInstance(Type.GetTypeFromCLSID(clsId, true));
}
catch (Exception e)
{
continue;
}
if (RemotingClassObject != null)
{
RemotingInterface = (IWinCalClient)RemotingClassObject;
if (RemotingInterface == null)
{
UnloadClient();
continue;
}
if (CanClientConnect(_RemotingInterface, connectToServer))
{
break;
}
}
if (Marshal.IsComObject(RemotingClassObject))
{
Marshal.FinalReleaseComObject(RemotingClassObject);
}
RemotingClassObject = null;
}
return RemotingInterface;
}
Update on the exception
After correcting the "bitness" of the test COM Client application that #SimonMourier clued me to, I am able to get the interface from the .NET 5 COM server. I have updated the code from the method.
HOWEVER, I'm now struggling with getting the interface from the .NET Framework COM server in the same way I get it from the .NET 5 COM server. I successfully register it using RegAsm.exe, but I get the following exception:
System.InvalidCastException: 'Unable to cast object of type 'CMI.WinCalRemoting.cWinCalClient' to type 'CMI.WinCalRemoting.IWinCalClient'.'.
I've done an exhaustive search to try to find out how to fix the .NET Framework COM project so that it can be used in the same way that the .NET 5 COM server is used so that it doesn't matter whether the COM client is a .NET Framework or a .NET Core assembly.
I added a .NET Framework COM server project to the shared directory below to replicate what I'm seeing. With the .NET Framework COM server.
I also switched the test application to be 32-bit to replicate how our sister application will be using the COM servers.
All of the projects are located here:
.NET 5 COM Interop
Unable to Add .NET Framework COM Type Library Reference
For a .NET Framework client assembly, I've attempted to add a reference to the .NET Framework COM server that was registered with regasm.exe, but that fails with the following message:
I'm trying to reference dlls in my dotnet core project that are using Entity framework standard, not core.
The problem that i'm running into with dotnet core is that the connection strings that are defined in the appsettings.json are not being read by the referenced dlls, so I get the error:
Message=No connection string named 'Entities' could be found in the application config file.
In the dotnet core app I'm able to get the connection string using EF core with:
"ConnectionStrings": {
"Entities": "my connection info", // this works
},
services.AddDbContext...
This all works. The problem that I run into now is that a lot of our legacy code is .net standard, and i'd prefer to just continue to use them as libraries for now.
Where it fails in the referenced libraries is when
using (var context = new Entities()) // here
Do I need to modify the entity framework layer in the referenced libraries so that they can accept a connection string as a parameter? That would require a lot of updating very old code.
My application runs on .NET framework 4.7 and I'm using Entity Framework 6.1.3. Currently, my code uses some classes from the namespace System.Data.SqlClient such as SqlParameter. I want to switch to Microsoft.Data.SqlClient.
However, I'm not sure if EF6 is compatible with Microsoft.Data.SqlClient. This is an old article from Microsoft, it says that EF Core, EF 6 etc. haven’t yet made the transition to the new provider Microsoft.Data.SqlClient. So, I'm a bit confused.
Everything has been working well with System.Data.SqlClient for the below code
public async Task<ICollection<int>> GetChildCustomerIdsAsync(int customerId)
{
var sqlParameters = new List<SqlParameter>()
{
new SqlParameter("#CustomerId", customerId)
};
return await DbContext.Database.SqlQuery<int>("dbo.sp_GetChildCustomerIds #CustomerId=#CustomerId",
sqlParameters.ToArray()).ToListAsync().ConfigureAwait(false);
}
However, when I am switching to Microsoft.Data.SqlClient, I'm getting this error:
System.InvalidCastException: The SqlParameterCollection only accepts non-null SqlParameter type objects, not SqlParameter objects.
at System.Data.SqlClient.SqlParameterCollection.ValidateType(Object value)
at System.Data.SqlClient.SqlParameterCollection.AddRange(Array values)
at System.Data.Entity.Core.Objects.ObjectContext.CreateStoreCommand(String commandText, Object[] parameters)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryInternalAsync.d__6f`1.MoveNext()
No, EF 6 does not work with Microsoft.Data.SqlClient, but I have published a package that does.
NuGet package: ErikEJ.EntityFramework.SqlServer
Documentation: here and here
MS are planning it
https://github.com/dotnet/ef6/issues/823#issuecomment-948340657
We're planning for next year now, and so far this is tentatively in the plan.
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/plan#theme-clear-path-forward-from-ef6
The exception to this is that we plan to add support for using EF6
with Microsoft.Data.SqlClient. This will be limited to runtime
support. Use of the EF6 designer in Visual Studio will still require
System.Data.SqlClient.
In the meantime, you can try adding a provider created by ErikEJ. This should help.
NuGet package: ErikEJ.EntityFramework.SqlServer
Documentation: here and here
I am attempting to get access to the underlying JSON object (request body) within a service call in service stack. The reason I wish to do this is to create a PATCH version of an existing service, which will only attempt to update fields that were in the JSON. Seems simple enough:
public object Patch(AddUpdateContactRequest request)
{
var json = this.Request.GetRawBody();
var keysToUpdate = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(json);
return Put(request);
}
However, I get an exception from service stack when GetRawBody() is called (when service stack is calling in to .NET core):
Message: "Could not load type
'Microsoft.AspNetCore.Http.Internal.BufferingHelper' from assembly
'Microsoft.AspNetCore.Http, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60'."
Source: "ServiceStack"
StackTrace: " at ServiceStack.Host.NetCore.NetCoreRequest.GetRawBody() in
C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\NetCore\NetCoreRequest.cs:line
211"
TargetSite: {System.String GetRawBody()}
TypeName: "Microsoft.AspNetCore.Http.Internal.BufferingHelper"
It seems to be referencing version 3.0 of Microsoft.AspNetCore.Http, but this version does not have the namespace Microsoft.AspNetCore.Http.Internal.BufferingHelper. It looks like a version mismatch issue, but I cannot seem to make it work. I cannot see any dll version conflicts locally, I am implicitly referencing the latest version of .NET core and have the latest SDK/hosting package installed.
If this is .NET Core 3.0 this is due to a breaking change in .NET Core 3.0 which has been resolved in the latest v5.7.1 packages on MyGet.
I have a .net Core 3.0 application attempting to invoke .Net Framework 4.8 via reflection. Our goal is to read encrypted AppSettings.config (xml) with ConfigurationManager, which is not possible in .Net Core. Unencrypted yes, but encrypted (CipherData), no.
My reflection code that accesses a static class & method goes like this:
Assembly assembly = Assembly.LoadFrom(exeFullName);
MethodInfo method = assembly.GetType(nameSpacenClass).GetMethod(methodName);
if (method != null)
{
object rtnVal = method.Invoke(null, new object[] { jsonParms });
return rtnVal;
}
else
return null;
The method.Invoke errorred out with this exception message:
FileNotFoundException: Could not load file or assembly 'System.Configuration.ConfigurationManager, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
So I wrote a proxy program in .Net Framework 4.8, and use the same code to access the code that reads encrypted config via reflection, and it works fine.
Then, I coded .net Core 3.0 to access that proxy via reflection, and it fails. The proxy .Net Framework 4.8 has no references whatsoever. It is barebone. Yet core still requires ConfigurationManager even though the proxy does not. The Config program has only one reference - to System.Configuration.
Core 3.0 => Encrypted App.config => Failed
Core 3.0 => ConfigProgram 4.8 => App.Config => Failed
ConfigProgram 4.8 => App.config => Works
Proxy 4.8 => ConfigProgram 4.8 => App.Config => Works
Core 3.0 => Proxy 4.8 => ConfigProgram 4.8 => App.Config => Failed
I guess the referenced System.Configuration is not required for Reflection invoke by 4.8 but is required even though trough a barebone stepping stone, by .Net Core 3.0. Could someone help me please?
When you load an assembly built for .NET Framework via reflection on .NET Core, it does not load any parts of .NET Framework - just your assembly ("user cod"). And some parts may or may not work. In your case it fails because the requested classes / assemblies of the code compiled for .NET Framework is not part of .NET Core.
This is the reason why only the cases where you start a .NET Framework application to begin with - thus booting up the .NET Framework Runtime - work for you.