Solr.NET – Getting started

Hi Folks!

This blog post is regarding the basics of SOLR setup and using the SOLR.Net.

So, let us get started!

Download the SOLR 5.5 from below URL. (You can give it a try with latest version else first use one)

https://archive.apache.org/dist/lucene/solr/5.5.0/

  • Extract the downloaded file to some location. In my case it is D:\learning\Search in dot net\solr-5.5.0.
  • Setup JAVA_HOME environment variable. To do so, follow this link.
  • Open CMD as admin from the SOLR bin folder
  • You should find solr.cmd from SOLR bin folder. Run below command from cmd

>solr.cmd start

  • Open the below URL in any browser

http://localhost:8983/

  • Run below command to create the core

>solr.cmd create -c articles

Go to http://localhost:8983/solr/#/

And refresh the page.

You should see the core created as above.

  • Create .NET framework console app.
  • Right click project node, go to nuget manager. Search for Solr.Net and install the latest stable package.
  • Copy below Article Class to your source file.
public class Article
    {
        [SolrUniqueKey("articleid")]
        public string CourseId { get; set; }

        [SolrField("articleTitle")]
        public string CourseTitle { get; set; }

        [SolrField("articleDescription")]
        public int DurationInSeconds { get; set; }

        [SolrField("publishDate")]
        public DateTime ReleaseDate { get; set; }

    }

  • Import required namespaces.
  • Go to below as highlighted to confirm we don’t have any data.
  • Write below code in your project
 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Indexing the content");
            IndexArticles();
            Console.WriteLine("Searching the content");
        }

        private static void IndexArticles()
        {
            List<Article> allArticles = new List<Article>();
            FillAllArticles(allArticles);
            Startup.Init<Article>("http://localhost:8983/solr/articles");

            ISolrOperations<Article> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Article>>();

            foreach (Article article in allArticles)
            {
                solr.Add(article);
            }

            solr.Commit();
        }

        private static void FillAllArticles(List<Article> allArticles)
        {
            allArticles.Add(new Article { Articleid=Guid.NewGuid().ToString(), ArticleDescription="article 1 descripton", ArticleTitle="article 1 title", PublishDate=DateTime.Now});
            allArticles.Add(new Article { Articleid = Guid.NewGuid().ToString(), ArticleDescription = "article 2 descripton", ArticleTitle = "article 2 title", PublishDate = DateTime.Now });
        }
    }

  • Build and execute the project.
  • Now go back to Solr Dashboard and refresh the core. You should see the inserted data.
  • Now comment out the first two lines in main method
            //Console.WriteLine("Indexing the content");
            //IndexArticles();
  • We will now read the data

Copy below code in your main method. Build and run it in debug mode for now.

   Startup.Init<Article>("http://localhost:8983/solr/articles");

            ISolrOperations<Article> solr = ServiceLocator.Current.GetInstance<ISolrOperations<Article>>();

            Console.WriteLine("Please enter search phrase:");
            string userInput = Console.ReadLine();

            while (!string.IsNullOrWhiteSpace(userInput))
            {
                SolrQueryResults<Article> Articles = solr.Query(userInput);

                int i = 0;
                foreach (Article Article in Articles)
                {
                    Console.WriteLine(i++ + ": " + Article.ArticleTitle + " "+ Article.ArticleDescription);
                }

                Console.WriteLine(Environment.NewLine + "Please enter search phrase:");
                userInput = Console.ReadLine();
            }

  • Enter search phrase and enter, you will get below error

The reason is the configuration of article fields has multiValued = true by default.

  • Let us disable it from the configuration itself.

Go to article managed-schema file. In my case it is D:\learning\Search in dot net\solr-5.5.0\server\solr\articles\conf\managed-schema

Provide multiValued=”false” to the above fields.

Save it.

  • To pick up new configuration we should restart the SOLR server. Close the cmd running the solr.
  • Open cmd as admin from SOLR bin folder. Run below command from cmd

>solr.cmd start

  • Now rerun the console application. We should be able to search.

Happy Basic Indexing and Searching!

Build failure in Azure DevOps – scenario 1

Challenge/Issue: .NET Project was building successfully but fails on azure build server. Following is the error.
Error CS8652 The feature ‘top-level statements’ is currently in Preview and unsupported. To use Preview features, use the ‘preview’ language version.
Error CS1513 } expected
Error CS1022 Type or namespace definition, or end-of-file expected

Solution: Locally we usually build with Debug mode. In Azure it builds with release mode as configured. So, we tried build the solution with release mode and the issue is reproduced. It was related to conditional compiler constants which was declared for Debug mode only. We gave for release mode and it is able to build successfully in local as well as build server in release mode.

DefineConstants (.NET) – Defines conditional compiler constants.
Symbol/value pairs are separated by semicolons and are specified by using the following syntax:
symbol1 = value1 ; symbol2 = value2
The property is equivalent to the /define compiler switch.

Analysis/Steps to reproduce the issue:
Create sample console application. So, you have below code.

using System;
 namespace ConsoleApp1
 {
     class Program
     {
         static void Main(string[] args)
         {
             Console.WriteLine("Hello World!");
         }
     }
 }

Now select the project node, Project > Project properties. Click “Build” tab. Provide some value like my_first_project as shown below.

This will add entries in csproj as below.

Let’s say we used these constants for determining namespace as below.

using System;
 if my_first_project
 namespace ConsoleApp1
 elif my_second_project
 namespace ConsoleApp2
 endif
 {
     class Program
     {
         static void Main(string[] args)
         {
             Console.WriteLine("Hello World!");
         }
     }
 }

This builds successfully in debug mode. Now change the build mode to release.

Now the issue is reproduced. You should see the errors in Error List window.

To resolve this go to Project properties > Build tab. Change configuration to Release and provide constant in Conditional compilation symbols as shown below. Save it. You should now see no error, be able to build successfully.

This will update csproj as below.

Please let me know your feedback in comments below.

HTH

Config transformation with MSBuild

This post explains how to include environment specific config files in regular ASP.NET Framework Visual studio project and build the same. This is also called as config transformation. Let’s start.

Open the Web application project in Visual studio or a create a new one. I have used VS2019. But this works VS2010 onward.

Right click the solution. Select ‘Properties’ from context menu. Expand ‘Configuration Properties’. Click ‘Configuration’. Click button ‘Configuration Manager’.

In new dialog – Configuration Manager, Select ‘New’ from the drop-down ‘Active solution configuration’.

Provide the new environment name here. Let’s consider ‘staging’ for demo purpose. You may copy settings from existing configuration. I copied from ‘Release’ or you may create a new one.
Check the checkbox for ‘Create new project configurations’. This will add the configuration at project level with the same name. In this case it is ‘staging’. You may add a new solution platform from second drop-down ‘Active solution platform’. You already have ‘Any CPU’ and ‘x64’. I selected ‘Any CPU’. Basically it is all about combinations of ‘Active solution configuration’ values and ‘Active solution platform’ values. So, make sure for every possible combinations, configuration names are same both at solution level and project level.

Here we make sure, for combinations at solution level i.e. staging | Any CPU and staging | x64, we have ‘staging’ configuration selected at project level too as below. These mappings are very important. Note that the given configuration from command line or selected configuration from visual studio is nothing but the solution configuration. And whatever project configuration is mapped with the solution level configuration will be applied for the project config files transformation. So, project configuration name decides what configuration transform file to pick, since $(Configuration) represents the mapped project configuration and not the solution configuration. This can be complicated so to keep things simple just make sure the corresponding project configuration name matches the respective solution configuration name.

So, it should look like below for every possible combinations.

When you add a new configuration and close. Then the active solution configuration will change. Here from debug to staging will be set. So, if you don’t want this to happen then select the debug from drop-down of ‘Active solution configuration’ before you close this dialog else you can come back and change it.

Once you are done with the configuration manager, then go to Solution Explorer. Right click the web.config. Select ‘Add Config Transform’. Once you do this, Web.staging.config file would be added. Here you can add your settings or change the existing with respect to the base file – web.config. However if you add any other config file like app.config and right click them, you will not find this option. Please let me know in comments if you see it :). So, in this case we need to do it manually i.e. add required environment specific config files and add the details to csproj file.

So first, from Visual Studio, add the app.config file in root folder. Save all. Close the Visual Studio. Go to project folder where you see .csproj file. Create file named app.staging.config. Do this for all configuration if required. Good to have it created, otherwise it might throw error during build.

Now, open the csproj file in a notepad. Find below line.

<Content Include="app.config" />

Below this, add the entries for the newly added files as dependent files as shown below.

<None Include="app.staging.config">
  <DependentUpon>app.config</DependentUpon>
</None>
<None Include="app.release.config">
  <DependentUpon>app.config</DependentUpon>
</None>
<None Include="app.debug.config">
  <DependentUpon>app.config</DependentUpon>
</None>

Now most important part comes, which does the transformation of the config files. Apart from config file references in csproj file. For each of the base file, you need to add the TransformXml task. Use target named ‘AfterBuild’ as below.

  <Target Name="AfterBuild">
    <TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="$(OutputPath)web.config" />
	<TransformXml Source="app.config" Transform="app.$(Configuration).config" Destination="$(OutputPath)app.config" />	
  </Target>

Save and close the notepad.

Now, open the project in Visual Studio. You can see dependent files below app.config. You may override the settings in these dependent files.
Good to provide the configuration specific output folder path. Double click the properties. Go to build tab in left pane. Select the configuration from the drop-down and provide the Output path with backward slash in end.

Save all.

To build the solution, you may select the configuration from toolbar and right click the solution and select “Build solution” or Ctrl+Shift+B.

You could use the cmd to build the solution using MSBuild command.

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin>msbuild <sln file path>\<project>.sln /p:Configuration=staging /p:OutputPath=<path of folder where you want the build output>

That’s all. Hope you found this to be helpful. Please drop your comments below if any thoughts or suggestion.

Happy config transformation 🙂

Custom dependency resolver in regular ASP.NET Framework project

Challenge:

Implement dependency injection using IoC container – Inversion of control container (a.k.a DI container – Dependency Injection container) in ASP.NET MVC Framework project.

We will see how an IoC container like Microsoft.Extensions.DependencyInjection can be configured in the ASP.NET Framework project of MVC type.

Solution:

I have used Visual studio 2019, create a new project and add MVC and web API folder and code references. See below snapshots.

In this example, we have a property in a controller. We will try to inject IRepo instance to constructor of a controller. Consider below code for IRepo and DBRepo.

public interface IRepo
{
	string Content { get; set; }
}

public class DBRepo : IRepo
{
	private string _content;
	public string Content { get => this._content; set => this._content = value; }
}

Consider HomeController code as below.

public class HomeController : Controller
{
	public IRepo repo;

	public HomeController(IRepo vrepo)
	{
		this.repo = vrepo;
	}

	public ActionResult Index()
	{
		return View();
	}
}

This is only for demo purpose. You could provide your own inteface and its concrete class.

Install the nuget package – Microsoft.Extensions.DependencyInjection.

Add Global.asax file in the project if it does not exists.

Key place is the app_start in global.asax where our implementation is done. Most important code is as follows:

void Application_Start(object sender, EventArgs e)
{
	// Code that runs on application startup
	AreaRegistration.RegisterAllAreas();
	GlobalConfiguration.Configure(WebApiConfig.Register);
	RouteConfig.RegisterRoutes(RouteTable.Routes);

	var services = new ServiceCollection();
	services.AddTransient(typeof(IRepo), typeof(DBRepo));
	services.Add(typeof(HomeController), Lifetime.Transient);
	var defaultResolver = new DefaultDependencyResolver(services.BuildServiceProvider());

	/*
		Below code line is the key. Here we need to pass our instance implementing the desired IoC container. Since it accepts an instance of IDependencyResolver, we need to implement this interface. DefaultDependencyResolver code follows and implements the interface IDependencyResolver.

Basically, provides a registration point for dependency resolvers, using the specified dependency
        //     resolver interface.
	*/
	DependencyResolver.SetResolver(defaultResolver);  
}

And the implementation of DefaultDependencyResolver is as below.

public class DefaultDependencyResolver : IDependencyResolver
{
	private IServiceProvider serviceProvider;
	public DefaultDependencyResolver(IServiceProvider serviceProvider)
	{
		this.serviceProvider = serviceProvider;
	}

	public object GetService(Type serviceType)
	{
		return this.serviceProvider.GetService(serviceType);
	}

	public IEnumerable<object> GetServices(Type serviceType)
	{
		return this.serviceProvider.GetServices(serviceType);
	}
}

Above is the bare minimum code requied for implementing the dependency injection.

We could improvise as below and write some generic code for including all the controllers.

public class Global : HttpApplication
{
	void Application_Start(object sender, EventArgs e)
	{
		// Code that runs on application startup
		AreaRegistration.RegisterAllAreas();
		GlobalConfiguration.Configure(WebApiConfig.Register);
		RouteConfig.RegisterRoutes(RouteTable.Routes);

		var services = new ServiceCollection();
		ConfigureServices(services);
		var defaultResolver = new DefaultDependencyResolver(services.BuildServiceProvider());
		DependencyResolver.SetResolver(defaultResolver);
	}

	private void ConfigureServices(ServiceCollection services)
	{
		services.AddTransient(typeof(IRepo), typeof(DBRepo));

		services.AddMvcControllers("*");

		/*		services.AddControllersAsServices(typeof(Global).Assembly.GetExportedTypes()
		.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
		.Where(t => typeof(IController).IsAssignableFrom(t)
		|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
		*/
	}
}

Below is the implementation for the extension methods on IServiceCollection.

    public static class ServiceProviderExtensions
    {
        private const string DefaultControllerFilter = "*Controller";
        /*
        public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
        {
            foreach (var type in serviceTypes)
            {
                services.AddTransient(type);
            }

            return services;
        }
        */
        public static void AddMvcControllers(this IServiceCollection serviceCollection, params string[] assemblyFilters)
        {
            serviceCollection.AddMvcControllers(GetAssemblies(assemblyFilters));
        }
        private static Assembly[] GetAssemblies(IEnumerable<string> assemblyFilters)
        {
            var assemblies = new List<Assembly>();
            foreach (var assemblyFilter in assemblyFilters)
            {
                assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(assembly => IsWildcardMatch(assembly.GetName().Name, assemblyFilter)).ToArray());
            }
            return assemblies.ToArray();
        }

        private static bool IsWildcardMatch(string input, string wildcard)
        {
            return input == wildcard || Regex.IsMatch(input, "^" + Regex.Escape(wildcard).Replace("\\*", ".*").Replace("\\?", ".") + "$", RegexOptions.IgnoreCase);
        }
        public static void AddMvcControllers(this IServiceCollection serviceCollection, params Assembly[] assemblies)
        {
            serviceCollection.AddMvcControllers(assemblies, new[] { DefaultControllerFilter });
        }

        private static void AddMvcControllers(this IServiceCollection serviceCollection, IEnumerable<Assembly> assemblies, string[] classFilters)
        {
            var controllers = GetTypesImplementing(typeof(IController), assemblies, classFilters);

            foreach (var controller in controllers)
            {
                serviceCollection.Add(controller, Lifetime.Transient);
            }
        }

        public static void Add(this IServiceCollection serviceCollection, Type type, Lifetime lifetime)
        {
            switch (lifetime)
            {
                case Lifetime.Singleton:
                    serviceCollection.AddSingleton(type);
                    break;
                case Lifetime.Transient:
                    serviceCollection.AddTransient(type);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, null);
            }
        }

        private static IEnumerable<Type> GetTypesImplementing(Type implementsType, IEnumerable<Assembly> assemblies, params string[] classFilter)
        {
            var types = GetTypesImplementing(implementsType, assemblies.ToArray());
            if (classFilter != null && classFilter.Any())
            {
                types = types.Where(type => classFilter.Any(filter => IsWildcardMatch(type.FullName, filter)));
            }
            return types;
        }

        private static IEnumerable<Type> GetTypesImplementing(Type implementsType, params Assembly[] assemblies)
        {
            if (assemblies == null || assemblies.Length == 0)
            {
                return new Type[0];
            }

            var targetType = implementsType;

            return assemblies
                .Where(assembly => !assembly.IsDynamic)
                .SelectMany(GetExportedTypes)
                .Where(type => !type.IsAbstract && !type.IsGenericTypeDefinition && targetType.IsAssignableFrom(type))
                .ToArray();
        }

        private static IEnumerable<Type> GetExportedTypes(Assembly assembly)
        {
            try
            {
                return assembly.GetExportedTypes();
            }
            catch (NotSupportedException)
            {
                // A type load exception would typically happen on an Anonymously Hosted DynamicMethods
                // Assembly and it would be safe to skip this exception.
                return Type.EmptyTypes;
            }
            catch (FileLoadException)
            {
                // The assembly points to a not found assembly - ignore and continue
                return Type.EmptyTypes;
            }
            catch (ReflectionTypeLoadException ex)
            {
                // Return the types that could be loaded. Types can contain null values.
                return ex.Types.Where(type => type != null);
            }
            catch (Exception ex)
            {
                // Throw a more descriptive message containing the name of the assembly.
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to load types from assembly {0}. {1}", assembly.FullName, ex.Message), ex);
            }
        }
    }

    public enum Lifetime
    {
        Transient,
        Singleton
    }

Use the required namespaces.

Put a break-point in HomeController constructor. Run the application in debug mode, you will see the concrete instance DBRepo is assigned to IRepo.

That’s all. Please let me know if you found it to be helpful. If yes, then please do like it and share it with your colleagues. Also, do let me know if you have any suggestion in the comment section below. Do follow this blog if you want to receive notification for the future blog posts. Thank you so much.

Visual Studio Debug issue: .NET source code file not found

Challenge: Visual studio sometimes looks for .net framework source code while debugging. It would prompt to allow download of the source code file.

dotnetframeworksourcecode_debug

If you are also facing such an issue or you do not want this behavior, then here is the solution.

Solution: Open visual studio, go to Tools > Options > Debugging > Enable Just My Code — Check this one.

It should work. Just in case, also delete the files from obj folder and bin folder of the project. Delete files from Temporary ASP.NET Files of your .NET framework version. (C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files) Restart the IIS and visual studio.

Hope this helps.

Third-party authentication to an existing regular ASP.NET MVC project

Hi Folks, this post is regarding authentication implementation with third parties like Google, Facebook, etc. in ASP.NET MVC.

Challenge: To figure out what additional code changes visual studio does when ‘Individual User Accounts’ selected in ‘Change Authentication’ while creating a project.

Solution: This code changes obtained by comparing the difference between two projects – one created with default one ‘No Authentication’ and the other created with ‘Individual User Accounts’.

The intention of this post is to observe the code changes done for the ‘Individual User Accounts’. In case of an existing project with no authentication, one could set up the third party authentication by making code changes as per the differences observed in this post.

Indeed, one can install Identity NuGet packages but it won’t add source code for App_start, Controllers, Models, Views, and Startup.

Let’s see the difference.

ProjectDifference

Copy below to packages.config file and restore them. Or find them in NuGet manager and install them.

<package id="EntityFramework" version="6.2.0" targetFramework="net472" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.2" targetFramework="net472" />
<package id="Microsoft.AspNet.Identity.EntityFramework" version="2.2.2" targetFramework="net472" />
<package id="Microsoft.AspNet.Identity.Owin" version="2.2.2" targetFramework="net472" />
<package id="Microsoft.Owin" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Host.SystemWeb" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security.Cookies" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security.Facebook" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security.Google" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security.MicrosoftAccount" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security.OAuth" version="4.0.0" targetFramework="net472" />
<package id="Microsoft.Owin.Security.Twitter" version="4.0.0" targetFramework="net472" />
<package id="Owin" version="1.0" targetFramework="net472" />

Update the targetFramework value as yours.

Add the new source code files to your project. Click here to download the new files. Copy them to respective folders. Remember to update the namespace with your namespace.

Now to enable third party authentication, for instance, Google. Go to Startup.Auth.cs, uncomment below code and provide client id and client secret.

//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});

To obtain values for above two keys please refer https://developers.google.com/adwords/api/docs/guides/authentication.

Similarly, you can do for other providers.

Build and run, click login link to open the login page. You could see ‘Google’ login link.

Hope this helps to understand the additional code and how to set up authentication in already existing project manually from scratch.

PS: If you find any other challenge or have any suggestion that could improve or correct this article, please feel free to drop a comment and I will try to improve this article.

Setup Sonar in local step by step

Sonar Setup
SonarQube is the central place to manage code quality, offering visual reporting on and across projects and enabling to replay the past to follow metrics evolution.
In this post, we will see how to set up Sonar in local machine step by step (for .NET). So, let’s start.

  1. Create a folder somewhere. In my case, it is C:\sonar.
  2. Download SonarQuebe from here.
    sonardownload
    Sonar Download

    Extract the zip file and copy sonarquebe folder from it to C:\sonar.
  3. Rename it to sonarqube for simplicity.
  4. So, in my case, it is C:\sonar\sonarqube. This has sonarquebe folders like bin and others.
  5. Download Sonar CSharp plugin from here.
    sonarcsharp
    Sonar CSharp Plugin
  6. Copy the plugin jar file in C:\sonar\sonarqube\extensions\plugins.
  7. Download JRE from here. In my case, I have windows 64 bit. So, I downloaded Windows x64 Offline version.
  8. Install it. Note down the path of Directory where you have installed the Java JRE. In my case, it is C:\Program Files\Java\jre1.8.0_111.
  9. Set system environment variable JAVA_HOME with above path as value. Click here to see how to set an environment variable.
  10. Update system PATH variable with ;%JAVA_HOME%\bin. Click here to see how to update the PATH variable.
  11. Download Sonar Runner from here.
  12. Unzip the file and rename the inside folder to sonar-runner for simplicity. Copy the sonar-runner folder to C:\Sonar folder. So, in my case, it will be C:\sonar\sonar-runner which has a bin folder.
  13. Set system environment variable SONAR_RUNNER_HOME to C:\sonar\sonar-runner.
  14. Update system PATH variable with ;%SONAR_RUNNER_HOME%\bin. NOTE: Values are separated by a semi-colon (;).
  15. Go to C:\sonar\sonar-runner\conf. Open sonar-runner.properties in notepad. Uncomment below the line by removing hash.
    	sonar.sourceEncoding=UTF-8
  16. Go to C:\sonar\sonarqube\conf. Open wrapper.conf in notepad. Update wrapper.java.command as below.
    	wrapper.java.command=%JAVA_HOME%\bin\java
  17. Now We will create sonar project properties files for each project. This file will have information regarding the project source code location. So, let’s create a folder somewhere. E.g. C:\TestSonar. Go inside the new folder. Right-click and create an empty file named sonar-project.properties and provide following content.
    	sonar.projectKey=TestProject1
    	sonar.projectVersion=1.0
    	sonar.projectName=TestProject1
    
    	sonar.sources=E:/sandeep/websites/TestProject1
    	sonar.language=cs
    
    	sonar.projectBaseDir=E:/sandeep/websites/TestProject1
    	sonar.dotnet.visualstudio.solution.file=TestProject1.sln
    	sonar.dotnet.4.0.sdk.directory=C:/WIndows/Microsoft.NET/Framework/v4.0.30319
    	sonar.dotnet.version=4.0
    	sonar.working.directory=C:/TestSonar/sonarworkingdir

    We have provided sonarworkingdir directory as working directory. So, create sonarworkingdir folder in TestSonar folder. Basically, if you don’t provide this property then .sonar folder will be created within the project folder given in sonar.projectBaseDir.

    Note: Do provide forward slash in path.

  18. Go to C:\sonar\sonarqube\bin\windows-x86-64 and run StartSonar.bat with admin. Wait for some time till it is up and running.
    startsonar
    StartSonar.Bat
  19. Go to C:\TestSonar where you have created the sonar-project.properties file. Run cmd as an admin here and execute the sonar-runner command.
  20. You can view the Sonar dashboard by localhost:9000 in the browser.

FAQ:

  1. How to setup Sonar for multiple projects?
    sonarmultipleprojects
    Sonar Multiple Projects

    I would suggest creating new folders named after each project. Create sonar project properties files for each project in their respective folders which will have project specific information. This helpful when you have multiple projects source codes lying in different drives or folders. Then write a batch script that will start from TestSonar folder and go inside of TestProject folders and execute sonar-runner one by one.

    In case you have a parent folder where multiple projects folders are residing then you can refer this link.
  2. How to move “.sonar” folder created in Project folder?
    Use sonar.working.directory in sonar project properties file as shown in above example. Beware: the specified folder is deleted before each analysis. I did not see any side effects of this deletion so far.
  3. What is the difference between sonarquebe and sonar-runner?
    SonarQube (formerly just “Sonar”) is a server-based system. Of course, you can install it on your local machine (the hardware requirements are minimal). But it is a central server with a database. Analyses are performed by some Sonar “client” software, which could be the sonar-runner, the sonar ant task, the sonar Eclipse plugin etc. The analysis results can be automatically uploaded to the server, where they can be accessed via the sonar Web application.(Reference)
  4. How to resolve the Wrapper stopped error?
    Run task manager as admin, and end all java.exe tasks.
    Or run cmd as admin and execute below command.
    taskkill.exe /F /IM java.exe
  5. How to resolve: ERROR: Caused by: Start pointer [line=1, lineOffset=0] should be before end pointer [line=1, lineOffset=0].
    sonarfailure_startpointer
    Sonar Runner Failure

    It has a problem with C# plugin version. (Reference)
    Go to C:\sonar\sonarqube\extensions\plugins.
    Delete sonar-csharp-plugin-5.5.0.479.jar if it is there.
    Click here to download sonar-csharp-plugin-5.3.2-RC1.jar. (Reference)
    Copy sonar-csharp-plugin-5.3.2-RC1.jar to the plugins folder.
  6. Others properties in Sonar project property file
    Click here to read more about sonar properties file.

Hope this helps. Please do provide your valuable feedback in comments below. It will help me improve the blog content.

Special Thanks to my colleagues – Praveen Rekhapalli and Akshatha Shetty.

File content not deleted completely

Challenge: We had a program that requests new xml content and write it in files. One day we started getting xml parsing error while parsing xml content from a file.

Solution: Upon investigation we found that new content is written, but old content last few lines does not get deleted as new content lines were less than old content lines. If you have new content lines greater than old content lines then there wont be any issue.

First of all, lets regenerate issue. Following code does that.

        static void Main(string[] args)
        {
            Console.WriteLine("Enter file path: ");
            string filePath = Console.ReadLine();

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("Hello");
            stringBuilder.AppendLine("World");

            //Writing content first time
            WriteContent(filePath, stringBuilder.ToString());
            //File.WriteAllText(filePath, stringBuilder.ToString());

            Console.WriteLine(File.ReadAllText("temp.txt"));

            stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("Hi");

            //Writing content second time. Note here content is shorter than first time
            WriteContent(filePath, stringBuilder.ToString());
            //File.WriteAllText(filePath, stringBuilder.ToString());

            Console.WriteLine(File.ReadAllText(filePath));
        }

        private static void WriteContent(string file, string content)
        {
            FileStream fileStream = null;
            StreamWriter writer = null;
            try
            {
                //FileMode.Create and FileMode.OpenOrCreate 
                fileStream = File.Open(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
                writer = new StreamWriter(fileStream);
                writer.Write(content);
                writer.Flush();
            }
            catch (Exception)
            {
                //log error
            }
            finally
            {
                if (writer != null)
                    writer.Close();
                if (fileStream != null)
                    fileStream.Close();
            }
        }

Run above code and you can see oworld word is left. Not completely removed.

output1
Output 1 – FileMode.OpenOrCreate

OpenOrCreate

Specifies that the operating system should open a file if it exists; otherwise, a new file should be created. If the file is opened with FileAccess.Read, FileIOPermissionAccess.Read permission is required. If the file access is FileAccess.Write, FileIOPermissionAccess.Write permission is required. If the file is opened with FileAccess.ReadWrite, both FileIOPermissionAccess.Read and FileIOPermissionAccess.Write permissions are required.

Now remove FileMode.OpenOrCreate and provide FileMode.Create.
Run and it overwrites completely.

output2
Output 2 – FileMode.Create

Create

Specifies that the operating system should create a new file. If the file already exists, it will be overwritten. This requires FileIOPermissionAccess.Write permission. FileMode.Create is equivalent to requesting that if the file does not exist, use CreateNew; otherwise, use Truncate. If the file already exists but is a hidden file, an UnauthorizedAccessException exception is thrown

Summary:

  1. So always use FileMode.Create in File.Open() to overwrite content completely.
  2. You can also use File.WriteAllText() method.

References:

https://msdn.microsoft.com/en-in/library/system.io.filemode%28v=vs.110%29.aspx