Not able to update assembly reference

SyntaxEditor .NET Languages Add-on for Windows Forms Forum

Posted 14 years ago by Hu Xuenian
Version: 4.0.0284
Avatar
Dear Sir/Mdm,

We are using actipro in our product which work similar in function as Visual Studio. Our product allows user to Add and Remove Assembly Reference from a Project. We currently face a problem in refreshing the DotNetProjectResolver ExternalReferences.


The scenario is as below:
1. User add an assembly (asm1.dll) to the project. Below is the simplified code just to show the logic behind the UI operation just for your understanding only:

byte[] assemblyBytes = ReadAssemblyFileBinaries(@"C:\Version1\asm1.dll");
Assembly assembly1 = Assembly.Load(assemblyBytes);
resolver1.AddExternalReference(assembly1); //resolver1 is DotNetProjectResolver
2. User has made some changes to asm1.dll, say add a new Property "Age" to "Customer" class in this assembly. Thus he choose to remove the assembly reference from the project and then add the newly modified assembly to the resolver again.

resolver1.RemoveExternalReference(assembly1.FullName);

byte[] assemblyBytes = ReadAssemblyFileBinaries(@"C:\Version2\asm1.dll"); //new version
Assembly assembly2 = Assembly.Load(assemblyBytes);
resolver1.AddExternalReference(assembly2);
3. In the Actipro Syntax Editor, the newly added "Age" Property is not showing in the intellisense of "Customer" object. The intellisense list still give the same list as Version1 of the asm1.dll.

I need to know how to get the resolver refreshed when someone remove an assembly from the project and then add a new version of the same assembly back to project again.

Thanks.

Comments (10)

Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Hu,

My guess is the way you are loading the Assembly is causing the issue. You are loading the Assembly into the current AppDomain and it will forever stay there until the AppDomain is unloaded (app closed). Later when you try to refresh, since it's already loaded, it's probably not updating anything.

If you use our overloads for AddExternalReference that take a string file path, you should be able to overcome this. With that, we load the assembly reflection only in a separate AppDomain and release it right when done looking at it.

Hope that helps.


Actipro Software Support

Posted 14 years ago by Hu Xuenian
Avatar
Thanks for the quick response. Using file path did work and the intellisense is refreshed properly. But I have another question. What if the assembly is installed in GAC? So the scenario would be

1. User register an assembly (asm1.dll) to GAC.
2. User then add assembly reference to the project. The simplified code is as follow:

//because assembly from GAC does not have file path, I use assembly FullName and I 
//expect DotNetProjectResolver will resolve it from GAC.
resolver1.AddExternalReference("asm1, Version=1.0.0.0, Culture=neutral,PublicKeyToken=4bcada08c74d7bad");     
    
3. User updates asm1.dll to add "Age" property to "Customer" class in the assembly.
4. User uninstall previous version of asm1.dll from GAC and register the updated asm1.dll to GAC.
5. User then remove previous assembly reference from the project and add the new version of the assembly from the GAC. The simplified code is as shown in step 2.
6. The intellisense on Syntax Editor does not refresh properly.

How to tackle this scenario?

Thanks
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Hu,

Hmm, I'm wondering if it's possible in that scenario. Can you do a test and let us know the results. Please load the assembly from the GAC with your own code like:
Assembly a = Assembly.ReflectionOnlyLoad(gacAssemblyName);
Verify that whatever types that are in your original assembly build are there. Then update your GAC by removing the old build and installing a new one with changes.

Use the same line of code again and see if it does load an Assembly that has the changed types/members in it or not. Please let us know the result. Thanks!


Actipro Software Support

Posted 14 years ago by Hu Xuenian
Avatar
I have tried ReflectionOnlyLoad by it does not refresh to the new assembly. It still show the old intellisense list.

Assembly assembly1 = Assembly.ReflectionOnlyLoad("asm1, Version=1.0.0.0,  Culture=neutral,PublicKeyToken=4bcada08c74d7bad");     
                    
resolver1.AddExternalReference(assembly1);

resolver1.RemoveExternalReference(assembly1);

//remove the assembly from the GAC and then reinstall the updated assembly to the GAC
//and then add reference to the project again as below
Assembly assembly2 = Assembly.ReflectionOnlyLoad("asm1, Version=1.0.0.0,  Culture=neutral,PublicKeyToken=4bcada08c74d7bad");

resolver1.AddExternalReference(assembly2);
Intellisense still show the old assembly list.

Anyway out of this issue?

Thanks for your prompt support.
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Hu,

Sorry let me clarify more. I didn't mean for you to use our object model in my request.

I just meant load the assemblies like you did in your last code snippet and then use reflection (native .NET framework reflection, not our AddExternalReference) to see if the assembly was truly refreshed or not in what was loaded. Again, the first assembly should be in the GAC, then load it, examine it with native .NET reflection, then uninstall it from the GAC, install the new one, load that, and examine it to see if it has differing contents or not.


Actipro Software Support

Posted 14 years ago by Hu Xuenian
Avatar
Thanks for the clarification.

I wrote a small windows application with two buttons one is click after the assembly is first time installed to GAC, the other buttons is click after the assembly is uninstalled from GAC and the updated assembly is installed to GAC. In both reflection, the number of property is 1. Which means the assembly is not updated.

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //Click after the assembly is first installed in GAC
        private void btnFirstReflectionOnlyLoad_Click(object sender, EventArgs e)
        {
            Assembly asm = Assembly.ReflectionOnlyLoad("TestRefAsm, Version=1.0.0.0,  Culture=neutral,PublicKeyToken=4bcada08c74d7bad");
            MessageBox.Show(asm.GetTypes()[0].GetProperties().Length.ToString());
        }

        //Click after the assembly is updated and reinstalled in GAC
        private void btnSecondReflectionOnlyLoad_Click(object sender, EventArgs e)
        {
            Assembly asm = Assembly.ReflectionOnlyLoad("TestRefAsm, Version=1.0.0.0,  Culture=neutral,PublicKeyToken=4bcada08c74d7bad");
            MessageBox.Show(asm.GetTypes()[0].GetProperties().Length.ToString());
        }
Is this what you want me to try out?
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Hu,

Yes that's what I wanted you to try. It's effectively what we are doing with our code, but with one more change that could make a difference. We load the assemblies in a separate AppDomain that is created each time an assembly is loaded. So make a new AppDomain and in the code for that AppDomain, load the assembly and check to see if they are still the same or not.

If they are the same in that scenario (loading the assembly in to two separate AppDomains) then it would appear to be a limitation of .NET and nothing we can control.


Actipro Software Support

Posted 14 years ago by Simon Shaw - IT Operations Manager, Micromine Pty Ltd
Avatar
I was having the same issues. We create some helper code that is based on the state of the program to help make script writing easier. To get the intellisense to update, we had to clear the resolver's cache with the code below:

m_resolver.RemoveExternalReference(m_assembly.FullName);
var files = System.IO.Directory.GetFiles(m_resolver.CachePath, "TestRefAsm.*");
foreach (var file in files)
    System.IO.File.Delete(file);
// Rebuild new assembly.
m_resolver.AddExternalReference(m_assembly);
It's not the most elegant solution and could have some syncronisation issues if running multiple instances of the same program and they are both trying to run the above code simultaneously. This solution however did work for us.

From what I could tell, it seems to occur because the new assembly has the same version information as the previous assembly and so the resolver doesn't refresh its cache. It would be good if the cache could store additional information such as creation/modification dates, file sizes and/or hash codes to be able to determine if an assembly had changed.

[Modified at 09/13/2010 07:25 PM]

[Modified at 09/13/2010 07:26 PM]
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Craig,

We have an AssemblyCodeRepository.Refresh static method that is designed to be called for cases where you rebuild an assembly but keep its same version info. Check that out as it will probably be exactly what you need here.


Actipro Software Support

Posted 14 years ago by Simon Shaw - IT Operations Manager, Micromine Pty Ltd
Avatar
Thanks for the pointer. I haven't tested it yet but it looks exactly like what we need.
The latest build of this product (v24.1.0) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.