Log4Net is the most powerful login engine I have come across.
Logging is essential in each application>
A framework like Log4Net allows you to log to any place you want to:
File, database, etc….
In this blog I’m going to explain how to implement this engine in a simple Command Prompt project.

  1. Step 1 create a simple Command Prompt project in Visual Studio
    2. Install the package withe Nugget, just search for ‘log4net’
    3. Add a file to our project called: log4net.config
    4. Next to make this framework working we need to tell our application were to find it’s configuration, open the AssemblyInfo.cs file of your project and add the following line:
 
 [assembly: log4net.Config. XmlConfigurator(ConfigFile = "log4net.config")] 
 

Which point to our config file we just created.

  1. The Log4Net uses the concept off apenders which you could see as ‘handlers‘, this will become clear in a minute.
    5.  I’m going to use “ColoredConsoleAppenderClass
    https://logging.apache.org/log4net/release/sdk/html/T_log4net_Appender_ColoredConsoleAppender.html
 
   <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
 
  1. Each appender in the framework has mappings we need to define in our config.
    These mapping are linked to the kind of log level you are going to use:
    Warning, Error, Fatal, Info
    Let add our mappings into out config:
 <mapping>

      <level value="WARN"/>

      <foreColor value="Yellow"/>

    </mapping>

    <mapping>

      <level value="ERROR"/>

      <foreColor value="Red"/>

    </mapping>

    <mapping>

      <level value="FATAL"/>

      <foreColor value="White"/>

      <backColor value="Red" />

    </mapping>

    <mapping>

      <level value="INFO"/>

      <foreColor value="Cyan"/>

    </mapping>

    <mapping>

      <level value="DEBUG"/>

      <foreColor value="Green"/>

    </mapping>

  
  1. Next we need to determine how our log lines will look like
    In our config there is a section layout:
    <layout type="log4net.Layout.PatternLayout">

      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>

    </layout>
 
  1. Let’s wrap it up our complete config.xml:

<log4net>

  <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">

    <mapping>

      <level value="WARN"/>

      <foreColor value="Yellow"/>

    </mapping>

    <mapping>

      <level value="ERROR"/>

      <foreColor value="Red"/>

    </mapping>

    <mapping>

      <level value="FATAL"/>

      <foreColor value="White"/>

      <backColor value="Red" />

    </mapping>

    <mapping>

      <level value="INFO"/>

      <foreColor value="Cyan"/>

    </mapping>

    <mapping>

      <level value="DEBUG"/>

      <foreColor value="Green"/>

    </mapping>

    <layout type="log4net.Layout.PatternLayout">

      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>

    </layout>

  </appender>

  <root>

    <level value="DEBUG" />

    <appender-ref ref="ColoredConsoleAppender"/>

  </root>

</log4net>

 

9.Let’s see how our code look like:


using log4net;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;

namespace SBS.Blog.Log4Net
 {
 class Program
 {
 private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

static void Main(string[] args)
 {

Logger.Debug("This is a Debug statement");
 Logger.Info("This is a Info statement");
 Logger.Warn("This is a Warn statement");
 Logger.Error("This is a Error statement");
 Logger.Fatal("This is a Fatal statement");
 Console.ReadLine();
 }
 }
 }

  1. This will result in the following output:

2018-06-11 20_56_52-C__Users_Sebastian_source_repos_SBS.Blog.Log4Net_SBS.Blog.Log4Net_bin_Debug_SBS.

  1. If you want to use the EventLogAppender you could use the following configuration into our log4net.config
 <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >

       <applicationName value="MyApp" />

       <layout type="log4net.Layout.PatternLayout">

             <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />

       </layout>

</appender>

 

  1. If you want to write to a file you can use the following configuration

<appender name="FileAppender" type="log4net.Appender.FileAppender">

    <file value="${TMP}\log-file.txt" />

    <appendToFile value="true" />

    <lockingModel type="log4net.Appender.FileAppender+InterProcessLock" />

    <layout type="log4net.Layout.PatternLayout">

        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />

    </layout>

</appender>
 

The result of the FileAppender:

loggedtofile

 

I know there are already millions of blogs explaining this concept.
But still I wanted to make this clear because this is a pattern that all developers should know, but yet ….
So what is actually the Factory Pattern ?
The best definition I found and made the best sense to me was the following:

The factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.

So it actually a way of programming where you need objects, but do not have to specify which object you need ?

So how does this pattern actually works, and what do you need to implement this pattern? Three things…

  • A factory
  • An Interface
  • Classes that implement this interface

Basically it comes down to this:

The factory class returns objects/classes who implement  a certain this interface

Let’s look at this with the following example, I need a class that generates for me a transportation vehicle which can be a bike, a car, a boat, an airplane.
So I create an interface:

interface IVehicle
{
int CountNumberOfWheels();
void DisplayNumberOfWheels();
}

Then I create two classes who implement this interface:
Bike.cs

class Bike : IVehicle
{
public int CountNumberOfWheels()
{
return 2;
}

public void DisplayNumberOfWheels()
{
Console.WriteLine("This BIKE has'" + CountNumberOfWheels() + "' number of wheels");
}
}

Car.cs

class Car : IVehicle
{
public int CountNumberOfWheels()
{
return 4;
}

public void DisplayNumberOfWheels()
{
Console.WriteLine("This CAR has'" + CountNumberOfWheels() +"' number of wheels");
}
}

I create a class to easily determine which vehicle I want, this will be passed to my factory as parameter.
VehicleType.cs

public enum VehicleType
{
Car,
Bike
};

The last thing we need now to implement our Factory Pattern is our ‘factory’ of objects:
VechicleFactory.cs

public  class VechicleFactory
{
public static IVehicle GetVehicle(VehicleType vehicleType)
{
IVehicle vehicle = null;

switch (vehicleType)
{
case VehicleType.Bike:
return vehicle = new Bike();
case VehicleType.Car:
return vehicle = new Car();
}

return vehicle;
}
}

Notice that my factory actually returns a type of object that implemented interface.
Now I can start using my Factory Pattern:

class Program
{
static void Main(string[] args)
{
IVehicle vehicle= VechicleFactory.GetVehicle(VehicleType.Bike);
vehicle.DisplayNumberOfWheels();

vehicle = VechicleFactory.GetVehicle(VehicleType.Car);
vehicle.DisplayNumberOfWheels();

Console.ReadLine();

}
}

The output will look like this:

2018-06-05 08_32_06-c__users_je35608_source_repos_SBS.FactoryPattern_SBS.FactoryPatternExplained_bin

So to implement this type of design pattern we need three things:

  • An interface
  • A class that implements that interface
  • A factory that generates objects/classes based on that interface

But every object has his own interpretation of the methods of the interface.
If we would not use this pattern, and place this code for instance in one class called vehicle we would needs to use switch and if statement if we want to do stuff for a specific vehicle.
Further does pattern allow you do easily generate more vehicles in the future without having to modify the other classes.

Cheers,

In a SharePoint MVC hosted app the ‘spcontext.js’ files has a piece of code that will append each hyperlink with the ‘SPHostUrl’ attribute.

Great….

But I ran into the fact that the script was crashing for some reason…
I added this piece

if (authority == null)
return false;

Into the file so it does not do an UpperCase on the next line because of the fact that the object is null

Modified function:

// Appends SPHostUrl as query string to all the links which point to current domain.
function appendSPHostUrlToLinks(spHostUrl, currentAuthority) {
$(“a”)
.filter(function () {
var authority = getAuthorityFromUrl(this.href);

//ADDED PIECE OF CODE
if (authority == null)

return false;

if (!authority && /^#|:/.test(this.href)) {
// Filters out anchors and urls with other unsupported protocols.
return false;
}
return authority.toUpperCase() == currentAuthority;
})
.each(function () {
if (!getSPHostUrlFromQueryString(this.search)) {
if (this.search.length > 0) {
this.search += “&” + SPHostUrlKey + “=” + spHostUrl;
}
else {
this.search = “?” + SPHostUrlKey + “=” + spHostUrl;
}
}
});
}

 

 

I use this datatable ALL the time.
https://datatables.net

For one of my projects I needed tot disable the automaticlly sorting by default.
If your page loads the datatables will sort the first column automaticlly.
To disable this, but the following in your configuration:

“aaSorting”: []

 

This is my complete configuration:

$(‘#table_timesheets_overview’).DataTable(
{
dom: ‘Bft’,
“bSort”: false,
“aaSorting”: []
});

 

I needed to remove the content editor dropdown you sometimes see in the form.
The most efficient way to do this is to add een Content Editor WebPart to the form of your list.
This way you could also add any javascript to your edit form, or other things to your page.

Here is the code:

SPList list = web.Lists[LIST_NAME];
string EditFormURL = list.DefaultEditFormUrl;

web.AllowUnsafeUpdates = true;

SPFile file = web.GetFile(EditFormURL);

if (null != file)
{
      using (SPLimitedWebPartManager mgr = file.GetLimitedWebPartManagerPersonalizationScope.Shared))
{
if (null != mgr)
{
//create new webpart object
ContentEditorWebPart contentEditor = new ContentEditorWebPart();
//Add content to CEWP
XmlDocument xmlDoc = new XmlDocument();
XmlElement xmlElement = xmlDoc.CreateElement(“Root”);
xmlElement.InnerText = GetValidationScript().ToString();
contentEditor.Content = xmlElement;contentEditor.Content.InnerText = xmlElement.InnerText;

//Add it to the zone
mgr.AddWebPart(contentEditor, “0”, 0);
web.Update();

}
}
}
}

private StringBuilder GetValidationScript()
{

StringBuilder sbScript = new StringBuilder();
string sJQueryURL = “_layouts/JQuery.js”;//exact path of your js file
sbScript.Append(“<script src='” + sJQueryURL + “‘type=’text/javascript’></script>”);
sbScript.Append(“<script type=’text/javascript’>”);
sbScript.Append(“$(document).ready(function() { “);
sbScript.Append(@” $(‘select[id*=””ContentTypeChoice””]’).parent().parent().hide();”);
sbScript.Append(“}); “);
sbScript.Append(“</script>”);
return sbScript;

}

 

 

 

Actually very straight forward if you are a SharePoint Developer:

 

public class MUACSupplierPerformanceEventReceiver : SPFeatureReceiver
{

public const string ACTION_GENERATE_REPORT = “Generate report”;

// Uncomment the method below to handle the event raised after a feature has been activated.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

using (SPWeb spWeb = properties.Feature.Parent as SPWeb)

{

if (spWeb.ListExists(GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION))

{

spWeb.AllowUnsafeUpdates = true;

SPList spList = spWeb.Lists[GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION];

RemoveAction(properties);

SPUserCustomAction useraction = spList.UserCustomActions.Add();

useraction.Name = ACTION_GENERATE_REPORT;

useraction.Location = “EditControlBlock”;

useraction.ImageUrl = “/_layouts/Images/fcoscorecard.png”;

useraction.Url = “{SiteUrl}/_layouts/EuroControl/SupplierPerformanceQuestions.aspx?itemId={ItemId}”;

useraction.Sequence = 106;

useraction.Title = ACTION_GENERATE_REPORT;

useraction.Update();

spList.Update();

spWeb.AllowUnsafeUpdates = false;

}

}

});

}

catch (Exception ex)

{

SPTUlsLog.WriteError(ex, SPTUlsLog.Error);

}

}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

{

RemoveAction(properties);

}

private void RemoveAction(SPFeatureReceiverProperties properties)

{

try

{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

using (SPWeb spWeb = properties.Feature.Parent as SPWeb)

{

if (spWeb.ListExists(GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION))

{

spWeb.AllowUnsafeUpdates = true;

SPList spList = spWeb.Lists[GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION];

foreach (SPUserCustomAction action in spList.UserCustomActions)

{

if (action.Title.Equals(ACTION_GENERATE_REPORT))

{

action.Delete();

spList.Update();

break;

}

}

spList.Update();

spWeb.AllowUnsafeUpdates = false;

}

}

});

}

catch (Exception ex)

{

SPTUlsLog.WriteError(ex, SPTUlsLog.Error);

}

}

}

}

 

 

The image url is ignored in SharePoint 2013, because it does not use icons.

 

Hope it helps