Hi Guys,
it seems i have a big problem when running under .net5
Usually XPO does load assemblies when it loads records from the database - if the record/class in not loaded currently.
i debugged the source and it seems the code related is in the XPTypeActivator which calls DevExpress.Data.Utils.Helpers.LoadWithPartialName which does an Assembly.LoadWithPartialName.
but it seems that LoadWithPartialName does not work? i also made a simple console application - out Newtonsoft.Json.dll in the output - and calling Snippet
Codevar a = Assembly.LoadWithPartialName("Newtonsoft.Json");
But this fails - and it seems also XPO fails - and now existing applications fail because they do not load the assembly and loading records fail?
i dont know if this is probably related to XAF - but LoadWithPartialName raises an AssemblyResolve event - somehow does assemblies are not loaded currently.
So i did a small console application - when compiled with net4.8
Snippet
Assembly.LoadWithPartialName("MyModule");
returns the assembly - when switching to net50 - the method returns NULL
Hello Martin,
I could not reproduce this issue. The Assembly.LoadWithPartialName method works in a .NET 5.0 app if the requested assembly exists in the application directory.
Attached is my test project for your reference. In the DxSample.Core project, I do not use types from the DxSample.BusinessModel.Core project to avoid loading the corresponding assembly in AppDomain. Still, the assembly gets loaded when I evaluate an expression like follows: "<DxSample.BusinessModel.Employee>HireDate".
Please make sure that the MyModule assembly exists in your application folder.
Hi Uriah,
it will work as long as you directly reference the project. if you remove the
<ProjectReference Include="…\DxSample.BusinessModel\DxSample.BusinessModel.Core.csproj" />
https://www.screencast.com/t/sViJm28Zm9oZ
Hi Martin,
Thank you for the hint. I reproduced this behavior and will get back to you as soon as I have any news.
currently this is some kind of showstopper - as an application stops working if one has create database records in the past with an module which is no longer used.
Hi Martin,
The assembly locating logic of .NET Core/.NET 5 apps differs form the logic of .NET Framework apps: Default Probing. According to the documentation, a .NET Core app uses the *.deps.json file to determine where to look for files. A .NET Core app scans the entire app directory only if the *.deps.json file isn't present. We are still working for the correct solution to the issue you described.
I think maybe you are describing a different scenario than the one demonstrated in my example. In my example, adding a missing assembly to project references makes sense because a class declared in this assembly is used in code indirectly.
Do you mean a scenario where a class is never used (directly or indirectly), but the application still fails? If so, please give me more error details. It is possible that there is a quick solution for the second issue.
Hi Uriah,
in our core solution we never reference external xaf modules - many modules are loaded as plugins at runtime via config file for example
Xaf Core Product
-> Xaf Plugin ModulA
-> Xaf Plugin ModulB
-> Xaf Plugin ModulC
Plugins are defined for example in xaf config files Modules section.
Now there are situations where a customer does not need an plugin anymore - so the plugin is deactivated - but still is in the application directory. in an .net framework version - xpo had automaticly load the desired dll - and loaded the database records correctly - now we get exception about CannotLoadClassInfo - because xpo does not load the assembly anymore - i can also provide a callstack
CallStack below - the module in which this type is defined is of course in the application directory
DevExpress.Xpo.Exceptions.CannotLoadInvalidTypeException: "Cannot load invalid type 'VenDoc.Module.Production.ProductionOrderDocument'."
DevExpress.Xpo.v21.1.dll!DevExpress.Xpo.XPObjectType.GetClassInfo() Unbekannt
DevExpress.Xpo.v21.1.dll!DevExpress.Xpo.Helpers.ObjectCollectionLoader.ObjectLoader.ResolveClassInfo() Unbekannt
DevExpress.Xpo.v21.1.dll!DevExpress.Xpo.Helpers.ObjectCollectionLoader.ObjectLoader.LoadObject() Unbekannt
DevExpress.Xpo.v21.1.dll!DevExpress.Xpo.Helpers.ObjectCollectionLoader.InternalLoadObjects(DevExpress.Xpo.Helpers.ObjectCollectionLoader.LoadDataResult loadResult, bool useStubs) Unbekannt
DevExpress.Xpo.v21.1.dll!DevExpress.Xpo.Helpers.ObjectCollectionLoader.LoadObjects(DevExpress.Xpo.ObjectsQuery[] queries) Unbekannt
DevExpress.Xpo.v21.1.dll!DevExpress.Xpo.SimpleObjectLayer.LoadObjects(DevExpress.Xpo.Session session, DevExpress.Xpo.ObjectsQuery[] queries) Unbekannt
Hello,
Please give us additional time to discuss this situation. We will update this thread once we have any news.
Thanks,
Andrey
Hello Martin,
Please try the following code as a workaround:
string extraModulesPath = @"d:\Temp\ExtraModulesPath\"; AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext arg1, System.Reflection.AssemblyName arg2) => { if(!string.IsNullOrEmpty(extraModulesPath)) { String assemblyName = arg2.FullName.Split(',')[0]; DirectoryInfo extraModulesDirInfo = new DirectoryInfo(extraModulesPath); FileInfo[] files = extraModulesDirInfo.GetFiles(assemblyName + "*.dll", SearchOption.AllDirectories); if(files.Length > 0) { if(files.Length == 1) { return Assembly.LoadFrom(files[0].FullName); } else { Assembly result = null; foreach(FileInfo info in files) { arg1.LoadFromAssemblyPath(info.FullName); } foreach(Assembly curAssembly in arg1.Assemblies) { string loadedAssemblyName = curAssembly.GetName().Name; if(loadedAssemblyName == arg2.Name) { result = curAssembly; } } return result; } } } return null; };
or like
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (context, assemblyName) => { string p = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), assemblyName.Name + ".dll"); if(System.IO.File.Exists(p)) { return context.LoadFromAssemblyPath(p); } return null; };
We are looking for a possible solution to this issue. We will inform you about our progress. Thanks for your patience.
thx Dmitry - for now i used XAF's Snippet
ReflectionHelper.AddResolvePath(System.AppDomain.CurrentDomain.BaseDirectory);
Hello Martin,
We decided to keep the current logic for loading assemblies because the issue is caused by the way .NET Core loads assemblies. From what I gather, the ReflectionHelper.AddResolvePath method helped you get the desired behavior. I suggest using it.
That is not what i have hoped for - this behaviour breaks all kind of applications - not only our winforms app - but also any kind of webapi service / blazor application… so everyone has to implement some kind of workaround now for xpo on net5 ?
Hello, Noxe.
Dima asked me to jump in here.
To be honest, I cannot say that "I am unhappy" with this new .NET 5 behavior. As you remember from your old thread (XPO - Old business class assemblies may be loaded from the application folder or GAC (due to outdated XPObjectType records) and cause side effects), dozens of XAF/XPO users did not like this behavior in .NET Framework ( the unexpected loading of assemblies from the bin folder based on records in the XPObjectType table) and blamed XPO publicly and privately for years.
Taking into account the simple solution with the AddResolvePath call and weighing this situation for the product and all its users, I believe this is the right way to go. At least until we get more feedback from other users and confirm that this advanced plugin scenario is more popular than we thought.
What do you think or what would you do for your product if you were in my shoes?
Hi Dennis,
i for sure remember this old ticket - and i also thought about it before i started this ticket. but for me it was 2 different use cases - at least for me - while i understand for xpo it is the same issue / use case.
"What do you think or what would you do for your product if you were in my shoes?"
I think it should be well documented as a first step for all other users - some kind of .net breaking changes. and for me it is fine so far - les see if others will face such issues in future :)
Thank you for your feedback, Noxe - I've updated XPO - Old business class assemblies may be loaded from the application folder or GAC and cause side effects in .NET Framework apps (due to outdated XPObjectType records) for other XAF/XPO users.