How to get value from .csprj file

67 views
Skip to first unread message

Dmitry Dolotovskikh

unread,
May 13, 2014, 7:47:03 AM5/13/14
to resharpe...@googlegroups.com
Hi

I need to read red markered value from project file



How to do this? BTW, I have IProject object.

Matt Ellis

unread,
May 14, 2014, 5:14:11 AM5/14/14
to resharpe...@googlegroups.com

ReSharper doesn’t have a really clean way of getting at this information right now (there are new APIs in 9.0 to access and cache arbitrary properties from msbuild files). You have two options. You can implement IProjectFileDataCache, which allows you to extract data from the msbuild xml file and cache it. This is quite simple – take a look at ShouldUseHostCompilerProvider for a good example.

 

The downside is that it doesn’t help with properties that have a condition applied to them – it just provides the raw xml of the msbuild file. From your example, it looks like this will be ok for you. If you do need to get at a property that is conditional, you can use ProjectModelSynchronizer.GetProjectInfoByProject(iproject) to return an instance of VSProjectInfo. From here, you can use the VsHierarchy property to get at the Visual Studio defined IVsHierarchy interface. This is Visual Studio’s representation of the items in a project, including files, folders and references – it maps to the solution explorer, essentially. From this, you can use the MSBuildExtensions.GetBoolValue extension method to retrieve a build property.

 

Regards

Matt

--

Matt Ellis

Technical Evangelist

JetBrains

http://www.jetbrains.com

“Develop with pleasure!”

--
You received this message because you are subscribed to the Google Groups "resharper-plugins" group.
To unsubscribe from this group and stop receiving emails from it, send an email to resharper-plug...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dmitry Dolotovskikh

unread,
May 14, 2014, 6:10:52 AM5/14/14
to resharpe...@googlegroups.com, matt....@jetbrains.com
Thank you for explanation. Now I use anouther approach

        public static bool IsSandbox(this IProject project)
       
{
           
bool result = false;


           
if (project.ProjectFile != null)
               
using (Stream stream = project.ProjectFile.CreateReadStream())
               
{
                   
XDocument document = XDocument.Load(stream);
                   
XElement sandboxedSolutionNode = document.Descendants().FirstOrDefault(p => p.Name.LocalName == "SandboxedSolution");


                   
if (sandboxedSolutionNode != null && !String.IsNullOrEmpty(sandboxedSolutionNode.Value))
                        result
= sandboxedSolutionNode.Value.ToLower() == "true";
               
}


           
return result;
       
}

Matt Ellis

unread,
May 15, 2014, 11:30:16 AM5/15/14
to Dmitry Dolotovskikh, resharpe...@googlegroups.com

It might be worth switching to IProjectFileDataCache. This prevents the need to re-read the file – ReSharper already has it loaded, and you’ll be notified when the file changes, otherwise you can use your cached value.

 

Matt

Dmitry Dolotovskikh

unread,
May 19, 2014, 9:28:27 AM5/19/14
to resharpe...@googlegroups.com, Dmitry Dolotovskikh, matt....@jetbrains.com
Hi

I've implemented IProjectFileDataCache interface and my provider works like a charm. Thank you very much.
One question. Should I implement OnDataChanged method? If I do nothing and just return null, it also works well.
What is ProjectModelChangeUtil.OnChange method call do?
       
        public Action OnDataChanged(IProjectFile projectFile, object oldData, object newData)
       
{
           
if (oldData != null)
           
{
               
bool oldValue = (bool)oldData;
               
bool newValue = (bool)newData;


               
if (newValue != oldValue)
                   
return () => myLocks.ExecuteWithWriteLock(() =>
                   
{
                       
IProject project = projectFile.GetProject();
                       
Assertion.AssertNotNull(project, "project must not be null");
                       
ProjectModelChangeUtil.OnChange(projectFile.GetSolution().BatchChangeManager,
                           
new ProjectItemChange(EmptyList<ProjectModelChange>.InstanceList, project,
                                project
.ParentFolder, ProjectModelChangeType.PROPERTIES, project.Location,
                                project
.GetPersistentID()));
                   
});
           
}


           
return null;
       
}

Matt Ellis

unread,
May 20, 2014, 6:58:35 AM5/20/14
to Dmitry Dolotovskikh, resharpe...@googlegroups.com

You should implement OnDataChanged only if you need to do something when the data changes. E.g., if you need to re-run some analyses when the sandbox value changes, you should handle that in OnDataChanged. If there’s no way for the user to change this value while the project is open, then I suspect it’ll be fine to ignore the method and just return null.

 

The way the IProjectFileDataCache works is like this:

 

1.       In your constructor, inject a ProjectFileDataCache instance, store it in a field and call cache.RegisterCache(lifetime, this)

2.       Implement CanHandle to see if you should handle the project file (e.g. get the project and check to see if it’s sharepoint)

3.       BuildData is called with the xml of the project file, you return an object that represents your cached value(s). This could be a boolean, or a class. Don’t store anything in your class fields. ReSharper will cache this value

4.       If the object instance is different to the previously cached instance, your OnDataChanged method is called. You can decide what needs to happen in response to this change, and you should return it as an action that will get executed later (after all processing of the file completes)

5.       ReSharper will call your Write method, passing in the cached object. You serialise this to the BinaryWriter however you want to

6.       ReSharper will also call Read method at the appropriate time, and you should use the Binary Reader to recreate the object that you need to cache. Again, don’t store anything in class fields here

7.       To get at your cached data, call projectFileDataCache.GetData(this, project, default), passing in the project and a default value for your cached object (true/false, complex object, etc)

 

ProjectModelChangeUtil.OnChange is just a helper function that will batch up changes to the change manager. It ensures that all changes are “propagated”, e.g. if given a change notification for a project item, it makes sure that gets propagated up to the project itself. It then uses IProjectModelBatchChangeManager to post the notification, which will either add the change to a current transaction, or execute it in its own, new transaction. I.e. the change happens as part of a new batch, of gets added to the existing batch. The batch manager then posts it to the change manager, which is an event bus that components can subscribe to in order to be notified of changes.

 

You can see the change manager subscriptions as a graph from the internal menu (ReSharper -> Internal -> View Change Manager Graph). This will create and try to open a .graphml file, which you can view with a tool like yEd (you’ll need to reformat it in hierarchical layout to be any use). You can see how events will flow from one component to another. Once one component accepts a change, it can create a new change and broadcast it. For example the solution will broadcast “project model change” events such as when a reference is added or removed. Various components will subscribe to these events, and convert them to PSI events, which are published to any subscribers, and used to add assembly metadata to the PSI cache, to populate code completion, etc.

 

Unless you’re adding new subscribers, or need to notify existing components of a project change, you probably don’t need to do anything here.

 

Regards

Dmitry Dolotovskikh

unread,
Aug 26, 2016, 3:58:12 PM8/26/16
to resharper-plugins, dmitrydo...@gmail.com, matt....@jetbrains.com
Hi

After update to 2016.2 I lost reference to ProjectFileDataCache class.
What should I use instead?

Thanks.

Matt Ellis

unread,
Aug 29, 2016, 9:04:06 AM8/29/16
to resharper-plugins, dmitrydo...@gmail.com, matt....@jetbrains.com
It looks like this has been refactored to handle .csproj and project.json files. The ProjectFileDataCache has been split into interfaces that you can inject. Namely, IProjectFileDataCache for .csproj files, and IProjectJsonDataCache for project.json files.

These interfaces both inherit from IProjectFileDataProviderCache<T> which defines the RegisterCache and GetData methods. The T parameter is the type of underlying data for the project file - XmlDocument for .csproj and a json.net JObject for project.json. You can register your cache with RegisterCache, but you now need to implement IProjectFileDataProvider<TRaw, TData>, or use the deriving interfaces to make it a little easier - IProjectFileDataProvider<TData> for .csproj or IProjectJsonDataProvider<TData>. The TData is your data type that you want to cache. Your provider will still have the BuildData to create the object, Read and Write to persist that object in ReSharper's cache and OnDataChange, to be called when your TData returned from BuildData has changed.

Regards
Matt

Dmitry Dolotovskikh

unread,
Sep 3, 2016, 4:41:27 PM9/3/16
to resharper-plugins, dmitrydo...@gmail.com, matt....@jetbrains.com
Hi

Can I inherit from ProjectFileDataCacheImpl ?

Matt Ellis

unread,
Sep 5, 2016, 5:48:03 AM9/5/16
to resharper-plugins, dmitrydo...@gmail.com, matt....@jetbrains.com
No, you implement IProjectFileDataProvider.
Reply all
Reply to author
Forward
0 new messages