2

I'm working on a project which has dependencies like this:

App            references ServiceStarter
ServiceStarter references AppServiceCore
AppService     references AppServiceCore
AppService     references AppServiceInterface

*** I CANNOT CHANGE HOW THESE PROJECTS REFERENCE EACH OTHER SO PLEASE KEEP THAT IN MIND**

In AppStarter I don't want to reference AppService directly. Because that reference can be changed(which AppService to use, AppService1, AppService2 etc...).

How I decided to import the service project is by using a MSBuild Task like so:

<!-- only change ServicesImplProjectName to set the service implementation project -->
<PropertyGroup>
    <ServicesImplProjectName>Services</ServicesImplProjectName>
    <ServicesImplProjectPath>$(SolutionDir)$(ServicesImplProjectName)</ServicesImplProjectPath>
    <ServiceImplProjectFileType>csproj</ServiceImplProjectFileType>
    <!-- vbproj, csproj ... -->
</PropertyGroup>

<Target Name="CustomServiceStarterBeforeBuild" BeforeTargets="BeforeBuild">
    <MSBuild Projects="$(ServicesImplProjectPath)\$(ServicesImplProjectName).$(ServiceImplProjectFileType);" 
          Targets="Restore;Build;" 
          BuildInParallel="false" 
          Properties="BuildProjectReferences=true;Configuration=debug;Platform=$(Platform);SolutionDir=$(SolutionDir);"
          StopOnFirstFailure="true">
    </MSBuild>
    <ItemGroup>
        <ServicesImplFiles Include="$(ServicesImplProjectPath)\$(OutDir)\**\*.*" />
        <Content Include="@(ServicesImplFiles)">
            <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
    </ItemGroup>
</Target>

The problem is that by using the msbuild cli and passing (while being in the ServiceStarter folder):

msbuild -p:BuildProjectReferences=true -p:Configuration=debug -target:build ServiceStarter.csproj -p:SolutionDir=MyPAth\

everything works and I get "build successful", but when I try to build using VisualStudio 2022 UI's build action (right click on ServiceStarter and then click on build), I get the error that says:

CSC : error CS0006: Metadata file 'MyPath\ServiceInterface\bin\Debug\Common.dll' could not be found

whilst it's a dependency of AppService (using project references). But why does this happen? why using msbuild CLI works but visualstudio2022 (also tested on 2019) UI build button doesn't work??

Any, I mean any helps are appreciated!

  • This project uses .net4.8 if that matters.
  • you can actually reproduce this exact same problem, by creating an empty solution using .net4.8 and adding 5 projects with the exact same dependency graph. then adding my custom MSBuild task to your App.csproj file.
Jonathan Dodds
  • 2,654
  • 1
  • 10
  • 14
m.stm
  • 81
  • 6
  • What you are trying to do is DI (Dependency Injection) but you are trying to force the DI into the build. That is not a good approach to DI. As to why your project doesn't work as you expect, MSBuild has two phases: evaluation and execution. `Target`s don't 'run' until the execution phase. The VS IDE UI tries to take some shortcuts. It assumes it will find `ProjectReference`s during evaluation. Your `MSBuild` task, when it runs, doesn't even create a `ProjectReference`. – Jonathan Dodds Jul 02 '23 at 15:42
  • @JonathanDodds Thanks for your reply. Let's ignore if this system is doign DI right or wrong. What would you suggenst as a solution to get the same behaviour while using the ci or the visual studio builld. Again thanks for your time and reply! – m.stm Jul 02 '23 at 16:11
  • @JonathanDodds It seems its a bug with vsbuild that needs to have a build dependecy defined in .sln! The behaviour is not normat. see [this](https://stackoverflow.com/questions/5629981/whats-the-purpose-of-this-string-in-my-visual-studio-sln-file). The fact that MSBuild and VSBuild don't behave the same is bad design choice IMO. The only solution I come up with is to add a build dependency in .sln via vs UI or manually. – ClassY Jul 02 '23 at 16:36
  • "VSBuild" is not a different build system. Visual Studio uses MSBuild but, for responsiveness in the UI, Visual Studio tries to make some assumptions. It was different teams working to different purposes but I agree that Microsoft should never have allowed the behavior to vary. – Jonathan Dodds Jul 02 '23 at 16:42
  • Build dependencies should be defined in the project. The build order can be influenced by settings in the solution. – Jonathan Dodds Jul 02 '23 at 16:46
  • @JonathanDodds funny thing I test right now is that I added a reference from project A to B but removed the "automatically added lines" in .sln file which defined the build dependencies and everything still worked fine using VSBuild. It seems VSBuild proiriatizes the project files as well but only the projectreference tag and not the MSBuild task, why this is the case is something I have no idea about. That actually is a bad design choice. – ClassY Jul 02 '23 at 17:16
  • @ClassY There is no "VSBuild". Yes, references are via `ProjectReference` and `PackageReference` in the project file. The "build dependencies" in the SLN don't add dependencies. They only influence the build order. – Jonathan Dodds Jul 02 '23 at 17:24
  • @ClassY The `MSBuild` task is building another project. It is not creating a dependency that MSBuild is aware of. – Jonathan Dodds Jul 02 '23 at 17:25
  • @m.stm Your error is on `Common`, not `AppService`. I'm guessing that, regardless of whether there is an `IAppService` interface and regardless of which concrete `AppService` implementation you may want, `AppStarter` is using types from `Common`. If that is the case, then `AppStarter` should have a `ProjectReference` to `Common`. – Jonathan Dodds Jul 02 '23 at 17:36
  • @JonathanDodds There is one thing wrong with your assumption, the msbuild that VSBuild launches (I know it is NOT a different thing it just calls to MSBuild with some parameters) the MSBuild task. That task should behave exactly the same as the cli. that MSBuild taks builds service project and that project references Interface. Therefore interface should be built. I still don't see why it doesn't. You said "MSBuild task is building another project". That is correct. MSBuild is building Services and it should build it comepletly with the project references it has just as the cli does. – ClassY Jul 02 '23 at 20:23
  • @ClassY The behavior of the `MSBuild` task itself is not changing. You state "That task should behave exactly the same as the cli." It does. Are you a colleague of @m.stm? Do you have access to the same code? Provide a minimal example that shows the issue and lets work on it in chat. – Jonathan Dodds Jul 02 '23 at 22:45
  • @JonathanDodds [The behaviour **IS** different](https://learn.microsoft.com/en-us/visualstudio/msbuild/build-process-overview?view=vs-2022#visual-studio-builds-vs-msbuildexe-builds) as microsoft says so (also supported by the example and the said steps on how to reproduce the said behaviour in the question). After some research we realized that by setting the "BuildingInsideVisualStudio" (see the link to learn.microsoft) to false msbuild task on vs will behave the same as vsbuild. But some other problems start to occure after that on solution rebuild which m.stm will post tommorow. – ClassY Jul 03 '23 at 16:58
  • @ClassY You misunderstood. I agree that the build is different. The behavior of the [`MSBuild` task](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task?view=vs-2022) is not different. – Jonathan Dodds Jul 03 '23 at 17:20
  • @ClassY A key point in the Microsoft doc you link to is "Visual Studio manages the project build order for Visual Studio builds". – Jonathan Dodds Jul 03 '23 at 17:28

1 Answers1

1

After some digging around I found an article from microsoft that satates when you build/rebuild within the visual studio app, the behaviour is different from when you use MSBuild CLI directly. That is because of some properties that are sent along to MSBuild like BuildingInsideVisualStudio. If you set BuildingInsideVisualStudio to false then the problems mentioned in the question will disapear.

<MSBuild Projects="$(ServicesImplProjectPath)\$(ServicesImplProjectName).$(ServiceImplProjectFileType);" 
      Targets="Restore;Build;" 
      BuildInParallel="false" 
      Properties="BuildingInsideVisualStudio=false;BuildProjectReferences=true;Configuration=debug;Platform=$(Platform);SolutionDir=$(SolutionDir);"
      StopOnFirstFailure="true">

Notice the BuildingInsideVisualStudio=false part. Doing so could cause multiple MSBuild instances build the same project at the same time which is not intended for instance mutliple processes accessing the same file and causing the build to fail.

ClassY
  • 744
  • 5
  • 20