European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core 9.0 Hosting - HostForLIFE :: Sending Emails Over .NET Framework, and General Problems Using C# Code

clock November 11, 2024 09:19 by author Peter

Many times new developers stumble upon a very simple task, “sending emails” over the .NET framework. Well, I am not going to cover any specific language here (C# or VB.NET, Visual C++ you're also included), I will talk generally about the .NET framework and the assemblies exposed by the .NET framework for sending the emails, using your own SMTP server's settings, such as username/word combination, port number and (most especially) the hostname for your SMTP server.

Background
Emails stand for electronic mail and they are widely used on a regular basis for communication. You can send an email for sharing text data or you can send your albums over emails easily. Email has been a part of internet entertainment for a great time and people use a variety of email clients, some love online clients for example Gmail, Yahoo! and so on and some prefer an offline version of their email clients that use an internet connection to download the emails from a server, such as Thunderbird, Outlook and so on.

But the fact is that all of them use the same protocol for transferring emails over the internet network. In this article, I will talk about sending emails over the network, downloading the emails is a totally separate topic and would have a separate protocol working in the back end to download the emails from the server.

Sending the emails
Emails are sent using the SMTP protocol, over the internet. It is similar to the Hypertext protocol (not in the manner of communication, but in a way that is a protocol for communication). For more on SMTP, you can find yourself glad to read the Wikipedia page, I am not going in-depth of the protocol here, instead, I will just elaborate on the methods to send the emails over the .NET framework.
What does the .NET framework offer?

The .NET Framework (who is oblivious to that?) has many cool assemblies for us to work with, using our favorite languages, from C# to C++ and the assemblies in the .NET framework allow us to focus on the quality of the application and the logic, leaving the rest of the low-level coding to the framework itself, including and most especially the garbage collection like stuff and memory management.

.NET framework has a namespace, known as System.Net. This namespace is responsible for the network communication for the .NET applications. But we will be more concerned about the System.Net.Mail namespace, for working with the mail protocol that exposes the SmtpClient,MailMessage classes for us to easily just our data to the objects and send the email using the .NET framework.

Creating the module for sending email
Since the .NET framework exposes many frameworks to create your applications over, starting from something as basic as a Console application, to as much user-friendly as Windows Presentation Foundation. The interesting thing is that in the .NET framework, the same code can be used on the back-end of a Console app and the WPF application. So, the code that would be used to send the email in a Console application is just the same as you would be using for the WPF application. That is why I am not going to specify any framework, instead, I will use a Console application for our project, to be simpler to be understood and to focus more on the code instead. You can (in your own IDE) create any kind of application you want, from Windows Forms to WPF to a web application (using ASP.NET).

Once your application has been created, you can create a simple module (function; not to be confused with the VB.NET's Module). Inside that, you can write the following code, don't worry I will explain the code in the future section of the article.
// You should use a using statement
using (SmtpClient client = new SmtpClient("<smtp-server-address>", 25))
{
    // Configure the client
    client.EnableSsl = true;
    client.Credentials = new NetworkCredential("<username>", "<word>");
    // client.UseDefaultCredentials = true;

    // A client has been created, now you need to create a MailMessage object
    MailMessage message = new MailMessage(
        "[email protected]", // From field
        "[email protected]", // Recipient field
        "Hello", // Subject of the email message
        "World!" // Email message body
    );

    // Send the message
    client.Send(message);

    /*
     * Since I was using the Console app, that is why I am able to use the Console
     * object, your framework would have different ones.
     * There is actually no need for these following lines, you can ignore them
     * if you want to. SMTP protocol would still send the email yours.
     */

    // Print a notification message
    Console.WriteLine("Email has been sent.");
    // Just for the sake of pausing the application
    Console.Read();
}

Voila, (if you added correct details in the preceding code) you would have your emails sent to the destination without any trouble, apart from an internet connection trouble. Now let us dissect the code into pieces and understand what happened, then I will mention a few problems that arise in programming and cause havoc for new developers in understanding the entire process including the errors that are raised due to problems in connections and so on.
Explanation of the preceding code

The first step in the code is the usage of the using statement. In the .NET framework, you stumble upon various objects that use resources that need to be disposed of properly, or at least closed. For example, when a file is created it is required to call the Close() function before any other process can use that file, similarly, some processes require that you can a Dispose() function on them, to release all the resources. But you can use a using statement, to let the .NET framework take care of all of the objects that need such functions to be called themselves. For example, in the following code.

using (SmtpClient client = new SmtpClient())
{
    // code here
}


Is better than
SmtpClient client = new SmtpClient();
// code here..
client.Dispose();

Due to many specific factors, that I am not going to talk about here. That leaves the discussion about the using statement, you will find a few deeper details about using statements on MSDN documentation.

Next comes the SmtpClient object. The SmtpClient creates an object that establishes the connection between your machine and the SMTP server you're using. SmtpClient requires you to set up a few things in it.

  • The hostname is the name of your SMTP server's address in string format.
  • The Port that you will be using to connect, default is 25 (TCP port).
  • Most of the connections require that you set the SSL active. You can see that happening in our code too.
  • Credentials are required before you can use a service, most of the servers (Gmail, Outlook, and so on) require that you send a username/word combination to send email from your account using the SMTP protocol. That is why in most cases default credentials forward the developers into errors. We use a NetworkCredential object (from System.Net namespace) to our username/word to the server.

Since SmtpClient is disposable we're using it inside a using statement. We're about to send an email, and for that, we create an object called MailMessage and our data to it. MailMessage object can set the From, To, Subject, and Body fields of an email message and then can be sent. You can see in our example, that we're using the constructor to create the MailMessage object that would hold the data for our From, To, Subject, and Body fields.

Finally, we're sending the email using the Send() function. The interesting thing is, in a GUI framework such as WPF or Windows Forms, we should be using SendAsync for the sake of asynchrony in our application that would help us to create a fluid GUI for our application, otherwise, the application would stay stuck until the email has been sent and the control continues from this line of code. To learn more about asynchronous programming, please move to the MSDN link and learn more from there, they've got great content for beginners like you.

A few errors in the programming
Generally, there are always errors that developers miss and then they become about “Where did I miss it?”. Similarly, in sending the email and establishing a secure connection, there are usually many problems, some are syntax, some are logical, but I would talk about the connection errors that might be raised. I tried to raise some exceptions myself to share them with you here, for you to understand when these exceptions might cause problems for your environment.

Usually, the exceptions in the connection are raised only at the Send, or SendAsync method when the SmtpClient is not able to send your email successfully. It can be due to a connection problem, authentication problem, or any other problem.

Problems with SMTP hostname

A general problem can be the hostname that you're in to the client to connect to, it must be correct and without the “http://“. You might stumble upon such a problem.

Hostname could not be resolved, because it has “http://” in it. Just the smtp.gmail.com, if you're using Gmail as your SMTP server. Otherwise, you should contact the SMTP developers for their SMTP hostname.

This would be resolved, by making sure that the hostname is correct. Every SMTP provider has its own settings for its server. Make sure you're using the correct ones. This is the first problem you would stumble upon if you're going to get any error. Failure to send mail can also be raised if the Firewall is blocking the network.

Another problem with the SmtpClient is, if you're not using the correct port number, then the connection might not be established and the worst thing is that there won't be any exception raised. For example, use port number 295. The command would continue to execute without any success message or exception. Make sure you're using the correct port number, otherwise, use the default TCP port number; 25. Port number 25 always works for me.

Errors authenticating the user
Whereas servers require the correct authentication, it is necessary that you the correct and required authentication details to the server. The first stage is to enable the SSL over your connection. Usually, servers close the connection if the connection isn't over SSL. Recall the code in this article and see the enable SSL command as in the following.
client.EnableSsl = true;

After this, you should ensure that you're using the correct combination of your username and word. If they're incorrect, the server is free to close the connection. The following exception is raised if any of such (authentication) problems occur in your application.

The server requires an SSL connection or the correct username/word combination. Make sure you're not wrong in both of these scenarios. Once these problems are resolved (and other problems don't arise) your email will be sent and you will see a success message in your application. Mine showed me the following.

Email successfully sent! Success message in the Console application.

Points of Interest
In the .NET Framework, you can use the System.Net and its namespace to work with the network. For mailing, you use the System.Net.Mail namespace. System.Net.Mail exposes a SmtpClient object, that uses a hostname and a port to connect to the SMTP server for sending the emails. Some of the servers require an SSL connection and credentials (username/word combination).

MailMessage is the object you would use to send the email, you can fill this object with the From, To, Subject, and Body fields of your email message. SmtpClient would send this object, you can use Send or SendAsync methods to send an email, depending on your framework and the methods that you would use to send the email.

Exceptions are raised in SmtpClient when the code reaches the Send (or SendAsync) function. That is because connection problems occur at this stage, the server tells the .NET framework for the errors in sending the email, and the exception is raised. Usually, the exceptions are raised for the following factors.

  • The username/word is incorrect.
  • SSL is not enabled.
  • The hostname is not correct, so the SmtpClient was not able to establish the connection at all.
  • If the port number is incorrect, there is no error message at all. This is a tricky part for every developer. You can minimize this problem by using 25 (the default TCP port).

SmtpClient exposes the Dispose() function, which is why it is better to use the SmtpClient object in a using statement, not just as a simple (and ordinary) object to call dispose of over later. Using a statement lets you leave the release of the resources to the .NET framework itself.

For those looking for a VB.NET code, you can use the Telerik converter to convert your C# code to VB.NET code (or vice versa).

.NET allows you to use the same code over various frameworks and platforms that run over . NET. Such as WPF, Console app, and ASP.NET web applications. That is why you can use this code above in nearly all of your applications, no matter the software apps, web apps, or whatever client application you're creating until it runs over the .NET framework. Because these assemblies are present in .NET, not in the language itself.

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.

 



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Sitecore Pipeline to add rel="noopener noreferrer" to a General Link

clock November 4, 2024 06:55 by author Peter

This tutorial will show you how to use a custom pipeline processor to process fields in a Sitecore environment. In particular, we will deal with LinkField types to make sure that, for further security, external links have the rel="noopener noreferrer" tag. First, we'll look at the ExternalLinks class's C# code, which adds the required characteristics and processes the LinkField. The pipeline configuration that adds our custom processor into the Sitecore renderField pipeline will then be examined in the.config file.

You will know how to design and set up a custom pipeline processor in Sitecore to efficiently manage external links at the end of this lesson. Let's get started!

Pipeline C# Code
In this code we process a field in a Sitecore context, specifically handling LinkField types. It ensures that external links have the rel="noopener noreferrer" attribute added for security purposes.
public class ExternalLinks
{
    public void Process(RenderFieldArgs args)
    {
        Assert.ArgumentNotNull(args, "args");

        if (Sitecore.Context.Site.DisplayMode == Sitecore.Sites.DisplayMode.Edit)
            return;

        var field = FieldTypeManager.GetField(args.GetField());

        if (field is LinkField linkField)
        {
            if (linkField.IsInternal || string.IsNullOrEmpty(linkField.Url))
                return;

            args.Parameters.Add("rel", "noopener noreferrer");
        }
    }
}

Pipeline .config
And now we have here the configuration of the pipeline. This configuration sets up the ExternalLinks class to be executed as part of the renderField pipeline in Sitecore. It ensures that the ExternalLinks processor runs after the GetLinkFieldValue processor. Additionally, it includes a setting to control the protection of external links with a target="_blank" attribute.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            <setting name="ProtectExternalLinksWithBlankTarget" value="false" />
        </settings>
        <pipelines>
            <renderField>
                <processor
                    type="Foundation.Infrastructure.Pipelines.ExternalLinks, Foundation.Infrastructure"
                    patch:after="processor[@type='Sitecore.Pipelines.RenderField.GetLinkFieldValue, Sitecore.Kernel']" />
            </renderField>
        </pipelines>
    </sitecore>
</configuration>

Thanks for reading!

And that's it! We created a custom pipeline processor in Sitecore to handle LinkField types. The ExternalLinks class ensures external links include the rel="noopener noreferrer" attribute for security. We integrated this processor into the renderField pipeline, ensuring it runs after the GetLinkFieldValue processor. This setup helps maintain the security and integrity of external links in your Sitecore application.

If you have any questions or ideas in mind, it'll be a pleasure to be able to be in communication with you, and together exchange knowledge with each other.

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Explaining IAuthorizationFilter in .NET Core

clock October 28, 2024 07:45 by author Peter

We will discuss ASP.NET Core IAuthorizationFilter in this article.

What is the Authorization Filter?
The Authorization Filter allows us to apply authorization rules to controllers and actions within our application. Authorization Filters in ASP.NET Core are responsible for checking whether a user is allowed to perform an action or access a resource. These filters run before the action method is executed, ensuring the user has permission to access the method.

Authorization filters are executed after the routing but before model binding and other action filters. If the authorization fails (e.g., the user does not have the required permissions), the filter short-circuits the request, and the action method does not execute.
IAuthorizationFilter

In ASP.NET Core, IAuthorizationFilter is an interface that allows you to perform authorization checks on an action or a controller before the action executes. Implementing this interface gives you the ability to enforce security and authorization rules within your application.

Advantages of IAuthorizationFilter

  • Centralized Authorization Logic: By using filters, you can centralize your authorization logic, making it easier to maintain and manage. You can apply the same filter to multiple controllers or actions.
  • Separation of Concerns: Filters support the separation of concerns in your application. They promote clean architecture by isolating authorization logic from business logic.
  • Flexibility: Since filters can be applied at the action or controller level, you have the flexibility to handle different authorization requirements for different endpoints easily.
  • Access to Action Context: The `IAuthorizationFilter` provides access to the `ActionExecutingContext`, which contains information about the current request, the action is executed, and route data. This allows for dynamic authorization checks based on the context.
  • Built-in to ASP.NET Core: It is part of the ASP.NET Core framework, which means it is well-integrated with the rest of the framework and benefits from the built-in dependency injection features.
  • Chain of Responsibility: Multiple filters can be applied to a single action. This allows for a chain of responsibility pattern, where different filters can be called in a defined order.

Disadvantages of IAuthorizationFilter

  • Performance Impact: If your authorization logic involves heavy or synchronous operations (like database calls), it can slow down request processing. This is critical if the logic is not optimized or you make external calls during authorization checks.
  • Complexity in Conditional Logic: Complex authorization requirements that depend on various conditions may lead to complicated filter implementations, making it harder to read and maintain.
  • Limited to Action Execution: Authorization filters are only executed before the action method is called. You cannot use them to enforce rules after action execution, such as in the response pipeline.
  • Difficulty in Testing: If your authorization logic is embedded within the filters, unit testing may become more complex since you'll need to mock the entire filter environment.

Example

public class CustomAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // Implement your authorization logic here
        var user = context.HttpContext.User;

        if (!user.Identity.IsAuthenticated)
        {
            // User is not authenticated, return a 403 Forbidden response
            context.Result = new ForbidResult();
        }
    }
}


// Register the filter in Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add<CustomAuthorizationFilter>();
    });
}

// Above .net 6

builder.Services.AddScoped<CustomAuthorizationFilter>();

[ApiController]
[Route("[controller]")]
[ServiceFilter(typeof(CustomAuthorizationFilter))]
public class HomeController : ControllerBase
{
    [HttpGet]
    public IActionResult GetAuthorized()
    {
        return Ok("You are authorized!");
    }
}

 

 

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.

 



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Serialization and DeSerialization in C#

clock October 22, 2024 06:50 by author Peter

Serialization is the process of transforming an item into a format (XML, JSON, SOAP, or binary) that is suitable for easy storage or transmission. The opposite procedure, known as deserialization, transforms the serialized data back into an object.

Types of Serialization Process

  • XML Serialization
  • JSON Serialization
  • Binary Serialization
  • SOAP Serialization

1. XML Serialization
XML serialization in .NET involves the process of transforming an object into an XML format, which facilitates easy storage, transfer, or sharing across different systems. In contrast to binary or SOAP serialization, XML serialization primarily focuses on the public fields and properties of an object, thereby providing a more straightforward and transparent method for data representation.

Features

  • Transform an object into XML format.
  • The predominant form of serialization is utilized for interoperability.
  • Public properties and fields undergo serialization, while private ones remain excluded.
  • Frequently employed in web services, configuration files, and for storage that is easily readable by humans.

Step 1. Create a class for XML serialization like the one below.
EmployeeDetails.cs
public class EmployeeDetails
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public string Hobbies { get; set; }
    public string EmployeeType { get; set; }
}


Step 2. Serialize the class EmployeeDetails.cs and save it according to the specified path.
public partial class MainWindow : Window
{
    EmployeeDetails employee;

    // Specify the file path where the XML file will be saved
    string filePath = @"C:\Users\peter\Desktop\Peter\SerailizedPath\"; // Change this to your desired location
    public MainWindow()
    {
        InitializeComponent();
        employee = new EmployeeDetails
        {
            Name = "Peter",
            Age = 30,
            Address = "London",
            EmployeeType = "Permanent",
            Hobbies = "Cricket"
        };
    }
    private void XmlSerialization_Click(object sender, RoutedEventArgs e)
    {
        // Create the XmlSerializer for the EmployeeDetails type
        XmlSerializer serializer = new XmlSerializer(typeof(EmployeeDetails));
        // Serialize the object to the specified location
        using (StreamWriter writer = new StreamWriter(Path.Combine(filePath, "Employee.xml")))
        {
            serializer.Serialize(writer, employee);
        }
    }
}
Step 3. DeSerialize the class EmployeeDetails.cs like below.
private void XmlDESerialization_Click(object sender, RoutedEventArgs e)
{
    XmlSerializer serializer = new XmlSerializer(typeof(EmployeeDetails));
    using (StreamReader reader = new StreamReader(Path.Combine(filePath, "Employee.xml")))
    {
        EmployeeDetails employeeDetails = (EmployeeDetails)serializer.Deserialize(reader);
        MessageBox.Show($"Name: {employeeDetails.Name}, Age: {employeeDetails.Age}, Address: {employeeDetails.Address}, EmployeeType: {employeeDetails.EmployeeType}, Hobbies: {employeeDetails.Hobbies}");
    }
}

2. JSON Serialization

In C#, JSON serialization is the process of transforming an object into a JSON string, while deserialization involves converting a JSON string back into an object. The System.Text.Json or Newtonsoft.Json libraries can be utilized for these operations.Features

  • Transform an object into JSON format.
  • It is commonly utilized for web APIs, configuration files, and efficient data storage.
  • JSON serialization is supported by both the System.Text.Json and Newtonsoft.Json libraries.

Step 1. Create a class for JSON serialization like the one below.
EmployeeDetails.cs
public class EmployeeDetails
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public string Hobbies { get; set; }
    public string EmployeeType { get; set; }
}


Step 2. Serialize the EmployeeDetails.cs class into JSON format and save it to the specified location based on your requirements.
public partial class MainWindow : Window
{
    EmployeeDetails employee;

    // Specify the file path where the json file will be saved
    string filePath = @"C:\Users\peter\Desktop\Peter\SerailizedPath\"; // Change this to your desired location

    public MainWindow()
    {
        InitializeComponent();
        employee = new EmployeeDetails
        {
            Name = "Peter",
            Age = 30,
            Address = "London",
            EmployeeType = "Permanent",
            Hobbies = "Cricket"
        };
    }

    private void JsonSerialization_Click(object sender, RoutedEventArgs e)
    {
        string jsonString = JsonConvert.SerializeObject(employee, Formatting.Indented);
        File.WriteAllText(Path.Combine(filePath, "Employee.json"), jsonString);
        Debug.WriteLine("Object serialized to employee.json");
    }

    private void JsonDESerialization_Click(object sender, RoutedEventArgs e)
    {
        string jsonString = File.ReadAllText(Path.Combine(filePath, "Employee.json"));
        EmployeeDetails employee = JsonConvert.DeserializeObject<EmployeeDetails>(jsonString);
        MessageBox.Show($"Name: {employee.Name}, Age: {employee.Age}, Address: {employee.Address}, EmployeeType: {employee.EmployeeType}, Hobbies: {employee.Hobbies}");
    }
}

3. Binary Serialization
Binary serialization refers to the method of transforming an object into a binary format, enabling it to be saved in a file, transmitted across a network, or exchanged between applications. After the object has been serialized into binary, it can subsequently be deserialized, restoring it to its original state.

Features

  • Transform an object into a binary representation.
  • Optimized for size, though not easily interpretable by humans.
  • It necessitates that the object is annotated with the [Serializable] attribute.
  • Frequently employed for saving objects to storage or for communication between applications.

Step 1. Create a class for Binary serialization like the one below.EmployeeDetails.cs
[Serializable]
public class EmployeeDetails
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public string Hobbies { get; set; }
    public string EmployeeType { get; set; }
}


Step 2. Serialize the EmployeeDetails.cs class in binary format and save it to the specified location based on your requirements.
public partial class MainWindow : Window
{
    EmployeeDetails employee;

    // Specify the file path where the Binary file will be saved
    string filePath = @"C:\Users\Peter\Desktop\Peter\SerailizedPath\";  // Change this to your desired location

    public MainWindow()
    {
        InitializeComponent();
        employee = new EmployeeDetails
        {
            Name = "Peter",
            Age = 30,
            Address = "London",
            EmployeeType = "Permanent",
            Hobbies = "Cricket"
        };
    }

    [Obsolete]
    private void BinarySerialization_Click(object sender, RoutedEventArgs e)
    {
        BinaryFormatter formatter = new BinaryFormatter();

        using (FileStream stream = new FileStream(Path.Combine(filePath, "Employee.dat"), FileMode.Create))
        {
            formatter.Serialize(stream, employee);
        }

        Debug.WriteLine("Object serialized to employee.dat");
    }
}


Step 3. DeSerialize the class EmployeeDetails.cs like below.

[Obsolete]
private void BinaryDESerialization_Click(object sender, RoutedEventArgs e)
{
    BinaryFormatter formatter = new BinaryFormatter();
    using (FileStream stream = new FileStream(Path.Combine(filePath, "employee.dat"), FileMode.Open))
    {
        EmployeeDetails person = (EmployeeDetails)formatter.Deserialize(stream);
        MessageBox.Show($"Name: {person.Name}, Age: {person.Age}, Address: {person.Address}, EmployeeType: {person.EmployeeType}, Hobbies: {person.Hobbies}");
    }
}

4. SOAP Serialization

It refers to the method of transforming an object into the XML format defined by the Simple Object Access Protocol (SOAP), facilitating its transmission across a network, particularly within web services. While SOAP was widely utilized in earlier web service architectures, its prevalence has diminished in recent years, largely due to the increasing adoption of RESTful APIs and JSON serialization.

Features

  • Utilizes the SOAP (Simple Object Access Protocol) format.
  • Traditionally employed in web services.

Step 1. Add the “SoapFormatter” Nuget package as shown below.

Step 2. Create a class for soap serialization like the one below.

EmployeeDetails.cs

public class EmployeeDetails
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
    public string Hobbies { get; set; }
    public string EmployeeType { get; set; }
}


Step 3. Serialize the EmployeeDetails.cs class into Soap format and save it to the specified location based on your requirements.
public partial class MainWindow : Window
{
    EmployeeDetails employee;
    // Specify the file path where the Soap file will be saved
    string filePath = @"C:\Users\peter\Desktop\Peter\SerailizedPath\"; // Change this to your desired location
    public MainWindow()
    {
        InitializeComponent();
        employee = new EmployeeDetails
        {
            Name = "Peter",
            Age = 30,
            Address = "London",
            EmployeeType = "Permanent",
            Hobbies = "Cricket"
        };
    }
    private void SoapSerialization_Click(object sender, RoutedEventArgs e)
    {
        SoapFormatter formatter = new SoapFormatter();
        using (FileStream stream = new FileStream(Path.Combine(filePath, "Employee.soap"), FileMode.Create))
        {
            formatter.Serialize(stream, employee);
        }
        Debug.WriteLine("Object serialized to employee.soap");
    }
}

Step 4. DeSerialize the class EmployeeDetails.cs like below.
private void SoapDESerialization_Click(object sender, RoutedEventArgs e)
{
    SoapFormatter formatter = new SoapFormatter();
    using (FileStream stream = new FileStream(Path.Combine(filePath, "employee.soap"), FileMode.Open))
    {
        EmployeeDetails person = (EmployeeDetails)formatter.Deserialize(stream);
        MessageBox.Show($"Name: {person.Name}, Age: {person.Age}, Address: {person.Address}, EmployeeType: {person.EmployeeType}, Hobbies: {person.Hobbies}");
    }
}


Note. The results of all the aforementioned formats can be observed by executing the application as demonstrated below. Various serialization methods cater to distinct use cases influenced by requirements for performance, interoperability, and readability.

Serialization methods

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Obtain Every SQL Server Instance in C#

clock October 18, 2024 08:13 by author Peter

We'll be using SQL SMO(SQL Management Objects).

First of All, you need to add a reference to the Microsoft.SqlServer.smo.dll file which is located in.

  • For 64-bit Windows 7:[Your drive]:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Smo.dll.
  • For 32-bit Windows 7:[Your drive]:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Smo.dll.

Now that we've added it to our project, we can start coding!

Add a ListBox control to your project.
DataTable dataTable = SmoApplication.EnumAvailableSqlServers(true);
listBox1.ValueMember = "Name";
listBox1.DataSource = dataTable;


By running this code alone,you'll get all the available sql servers inside your listbox control.

Ok, let's develop it further. Let's see what databases our instances have. So to do this, you need to add another ListBox.Then in your listbox1's selectedindexchanged event, you need to check which server selected. So you need to create a server object first.

After this, you'll iterate through this server to populate all the databases in the newly created Listbox.

Here is the code to do that.
listBox2.Items.Clear();
if (listBox1.SelectedIndex != -1) {
    string serverName = listBox1.SelectedValue.ToString();
    Server server = new Server(serverName);
    try {
        foreach (Database database in server.Databases) {
            listBox2.Items.Add(database.Name);
        }
    } catch (Exception ex) {
        string exception = ex.Message;
    }
}

After we run the project we'll be getting our Databases.

Hope it helps!

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: ASP.NET Core: Customizing HTTP Headers with Middleware

clock October 14, 2024 08:04 by author Peter

We'll attempt to use custom middleware to alter HttpResponse in this post. After developing custom middleware, we will alter the response. The middleware will alter the HttpResponse each time the request is returned to the client.

public class CustomResponseMiddleWare
{
    private readonly RequestDelegate _next;
    public CustomResponseMiddleWare(RequestDelegate next)
    {
        _next = next;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        context.Response.Headers["CustomHeader"] = "This middleware added by Peter";
        await _next(context);
    }
}

We have created custom middleware and added a new header at the invoke method.

Program.cs
using SampleHttpResponseModify;

var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Use custom middleware
app.UseMiddleware<CustomResponseMiddleWare>();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();


Register middleware at the program. cs.
app.UseMiddleware<CustomResponseMiddleWare>();

When we ran the application and inspected it, we found a custom-added header in the response.Now let us try to modify the Http header when a particular API is called.
Consider i want to add an extra header when we call weather API only then do we have to add condition on middleware like below.
public class CustomResponseMiddleWare
{
    private readonly RequestDelegate _next;
    public CustomResponseMiddleWare(RequestDelegate next)
    {
        _next = next;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        context.Response.Headers["CustomHeader"] = "This middleware added by Peter";
        if (context.Request.Path.Value == "/WeatherForecast")
        {
            context.Response.Headers["CustomeHeadernparticularRequest"] = "This is header added when this path found";
        }
        await _next(context);
    }
}

We have added the below condition only, so if the below path is found then only add an extra header.
if (context.Request.Path.Value == "/WeatherForecast")
{
    context.Response.Headers["CustomeHeadernparticularRequest"] = "This is header added when this path found";
}

Response on WeatherForcast API. We can see the additional header is coming.

Another learning point here is a sequence of middleware in the program.cs. If we move the position of UseMiddleware then we will find that this middleware is not calling due to incorrect positions or order of middleware.

Conclusion
We have learned about how to modify http headers using custom middleware. Keep learning and Keep Enjoying.

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: ​Simple Load Balancer in .NET Core with YARP

clock October 8, 2024 08:55 by author Peter

Load balancing is essential in distributed systems and web applications to ensure that traffic is efficiently distributed across multiple servers or resources.

Reverse Proxy

A reverse proxy is a server that sits between client devices (e.g., browsers) and the backend servers, forwarding client requests to the appropriate server and then returning the server's response to the client.

Sticky Sessions

Sticky sessions (also known as Session Affinity) in YARP is a feature that ensures requests from a particular client are always routed to the same backend server. This is particularly important for applications that rely on server-side session data, such as when storing user state or authentication information in memory on specific servers.

YARP(Yet Another Reverse Proxy) Setup

create a new ASP.NET Core web appliLoad balancing is essential in distributed systems and web applications to ensure that traffic is efficiently distributed across multiple servers or resources.

Reverse Proxy

A reverse proxy is a server that sits between client devices (e.g., browsers) and the backend servers, forwarding client requests to the appropriate server and then returning the server's response to the client.

Sticky Sessions

Sticky sessions (also known as Session Affinity) in YARP is a feature that ensures requests from a particular client are always routed to the same backend server. This is particularly important for applications that rely on server-side session data, such as when storing user state or authentication information in memory on specific servers.

YARP(Yet Another Reverse Proxy) Setup

  • create a new ASP.NET Core web application
  • Install the Yarp.ReverseProxy NuGet package

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();
app.MapReverseProxy();

app.Run();


appsettings.json
{
  "ReverseProxy": {
    "Routes": {
      "api-route": {
        "ClusterId": "api-cluster",
        "Match": {
          "Path": "{**catch-all}"
        },
        "Transforms": [
          { "PathPattern": "{**catch-all}" }
        ]
      }
    },
    "Clusters": {
      "api-cluster": {
        "SessionAffinity": {
          "Enabled": "true",
          "AffinityKeyName": "Key1",
          "Cookie": {
            "Domain": "localhost",
            "Expiration": "03:00:00",
            "IsEssential": true,
            "MaxAge": "1.00:00:00",
            "SameSite": "Strict",
            "SecurePolicy": "Always"
          }
        },
          "LoadBalancingPolicy": "RoundRobin",
          "Destinations": {
            "destination1": {
              "Address": "https://localhost:7106"
            },
            "destination2": {
              "Address": "https://localhost:7107"
            }
          }
        }
      }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Destination Address: The destination address should point to the base URL of your hosted web application.

Output
Here is the console output of round-robin policy.

Load balancer

Summary
Load balancing helps in improving the scalability, performance, availability, and security of web applications and services.

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: ​Implementing CORS in .NET Core 8

clock October 4, 2024 07:56 by author Peter

Web pages are prohibited from sending requests to a domain other than the one that provided them thanks to a security feature called Cross-Origin Resource Sharing, or CORS. Building safe and useful apps in modern web development requires knowing how to properly implement CORS, especially when utilizing Angular as a front-end framework and.NET Core 8 as a backend. The best practices and typical pitfalls for configuring CORS in a.NET Core 8 environment are described in this article.

Assume you have a friend who lives next door (another website) and a toy box at home (your website). Your mom (the browser) has a rule that states your friend can only play with their own toys. Your friend wants to play with your toys (data) in your toy box. This is to ensure that everything is secure and safe. Assume that your friend's website is funfriend.com and that yours is mycooltoys.com. CORS checks are performed if funfriend.com requests to borrow a toy from mycooltoys.com.

  • Is funfriend.com allowed to borrow toys from mycooltoys.com?
  • Did they follow the rules about how to ask?
  • If everything is good, then your friend can borrow the toy!

Setting Up CORS in .NET Core 8

  • Install Necessary Packages: If you haven't already, ensure you have the required .NET Core packages installed. In most cases, the default setup will suffice, but you can install any specific CORS libraries if needed.
  • Configure CORS in Startup: In .NET Core 8, the configuration of CORS is typically done in the Program.cs file. Here’s a simple setup.

    var builder = WebApplication.CreateBuilder(args);
    // Add CORS services
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAngularApp",
            builder => builder.WithOrigins("https://your-angular-app.com")
                              .AllowAnyMethod()
                              .AllowAnyHeader()
                              .AllowCredentials());
    });
    var app = builder.Build();
    // Use CORS policy
    app.UseCors("AllowAngularApp");
    app.MapControllers();
    app.Run();


  • Allowing Specific Origins: For production environments, it’s crucial to specify the exact origin rather than using AllowAnyOrigin(), which is a common pitfall. Limiting allowed origins enhances security.

options.AddPolicy("AllowAngularApp",
    builder => builder.WithOrigins("https://your-angular-app.com")
                      .AllowAnyMethod()
                      .AllowAnyHeader());

  • Handling Preflight Requests: Ensure your server can handle preflight requests. These are OPTIONS requests sent by browsers to check permissions. By enabling CORS and handling these requests, you ensure that your application can respond correctly.
  • Allow Credentials: If your Angular application needs to send cookies or HTTP authentication information, you need to set AllowCredentials() in your CORS policy. Be cautious with this feature, as it requires that the origin is explicitly specified and cannot be set to AllowAnyOrigin().

Advanced CORS Configuration

  • Customizing Allowed Methods: You can customize allowed methods if your API uses specific HTTP methods.

    options.AddPolicy("AllowAngularApp",
        builder => builder.WithOrigins("https://your-angular-app.com")
                          .WithMethods("GET", "POST", "PUT", "DELETE")
                          .AllowAnyHeader()
                          .AllowCredentials());


Setting Exposed Headers: If your API returns custom headers that the client needs to access, specify these using WithExposedHeaders.

options.AddPolicy("AllowAngularApp",
    builder => builder.WithOrigins("https://your-angular-app.com")
                      .AllowAnyMethod()
                      .AllowAnyHeader()
                      .WithExposedHeaders("X-Custom-Header")
                      .AllowCredentials());

Logging CORS Requests: For debugging purposes, you can log CORS requests to track any issues that arise. Here’s a simple logging middleware.
app.Use(async (context, next) =>
{
    if (context.Request.Headers.ContainsKey("Origin"))
    {
        var origin = context.Request.Headers["Origin"];
        Console.WriteLine($"CORS request from: {origin}");
    }
    await next();
});

Best Practices

  • Limit Origins: Always specify the exact origins that are permitted to interact with your API. Avoid using wildcards (*) as they expose your API to potential security risks.
  • Use HTTPS: Ensure both your .NET Core backend and Angular frontend are served over HTTPS. This secures data in transit and enhances trustworthiness.
  • Regularly Review CORS Policies: As your application grows and evolves, periodically review your CORS configurations to ensure they align with current security requirements.
  • Test CORS Configurations: Use tools like Postman or browser developer tools to test your CORS setup. Check for errors and ensure your API is returning the expected headers.
  • Document Your API: Clearly document the CORS policies and allowed origins in your API documentation. This helps other developers understand how to interact with your API correctly.

Common Pitfalls

  • Misconfigured Allowed Origins: One of the most frequent mistakes is misconfiguring allowed origins. Double-check the exact URLs, including the protocol (HTTP vs. HTTPS) and any potential trailing slashes.
  • Forgetting to Apply CORS Middleware: Ensure that the UseCors middleware is applied before any endpoints are mapped. Placing it after endpoint mapping can lead to unexpected behaviors.
  • AllowAnyOrigin with AllowCredentials: This combination is not allowed and will cause CORS requests to fail. If you need credentials, specify the exact origins.
  • Not Handling OPTIONS Requests: Ignoring the preflight OPTIONS requests can lead to issues when your API is accessed from different origins. Ensure your server can properly handle these requests.

Example
Default

Allowing Specific Origins

Conclusion
Implementing CORS in .NET Core 8 for Angular applications is crucial for creating a secure and functional web application. By following best practices and being aware of common pitfalls, you can ensure that your CORS setup is both effective and secure. Regularly revisiting your CORS configuration as your application evolves will help maintain security and functionality in the long run.

Happy Coding!

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Using the Shopping Cart Discount Function to Better Understand the Concept of a Rule Engine

clock September 30, 2024 09:30 by author Peter

Hello everyone, I hope everything is going well for you. We will examine the idea of a rule engine in this post, as well as how NRule (a.NET package) implements it. We will develop a new.NET 8 desktop application to demonstrate the shopping cart discount feature for users according to their membership status (Normal/Silver/Gold/VIP) in order to facilitate comprehension.

Now let's use NRule and Rule Engine together

A software program that carries out one or more business rules in a runtime production setting is called a rule engine. The policies may be derived from corporate guidelines, statutory regulations, or other sources. You may think of a rule engine as an advanced translator of if/then statements. A rule engine's main advantage is that its business rules may be written in a non-programmer's language and can be changed without affecting the application code underneath.

NRules - An Introduction
NRules is an open-source .NET rule engine that supports defining business rules separate from the system logic. It allows for rules to be written in C#, using internal DSL to express rule conditions and actions. NRules is highly extensible, can integrate with any .NET application, and supports dynamic rule compilation.
Shopping Cart Discount Logic Using NRules

In this example, we will create a simple console application in C# that uses NRules to apply different discount percentages based on the user's membership type (Normal, Silver, Gold, VIP).

Step 1. Set Up the Project
First, create a new Console Application in Visual Studio or using the .NET CLI. Then, add the NRules package and NRules.Runtime package via NuGet.

dotnet add package NRules
dotnet add package NRules.Runtime

Step 2. Define the Domain Model
Create classes for Customers and Orders
public class Customer
{
    public string MembershipType { get; set; }
}

public class Order
{
    public Customer Customer { get; set; }
    public decimal TotalAmount { get; set; }
    public decimal DiscountedAmount { get; set; }
}


Step 3. Define the Rules
Create a class MembershipDiscountRule for discount rules. We'll consolidate the rules into a single class for simplicity.
using RuleEngine_ShoppingCartDiscount.Models;
using NRules.Fluent.Dsl;
using System;

namespace RuleEngine_ShoppingCartDiscount
{
    public class MembershipDiscountRule : Rule
    {
        public override void Define()
        {
            Order order = null;

            When()
                .Match<Order>(() => order,
                              o => o.Customer != null,
                              o => o.DiscountedAmount == 0); // Ensure discount is not already applied

            Then()
                .Do(ctx => ApplyDiscount(order))
                .Do(ctx => ctx.Update(order));
        }

        private void ApplyDiscount(Order order)
        {
            var discount = order.Customer.MembershipType switch
            {
                "Normal" => 0.90m, // 10% discount
                "Silver" => 0.80m, // 20% discount
                "Gold" => 0.75m, // 25% discount
                "VIP" => 0.70m, // 30% discount
                _ => 1.00m // No discount
            };

            order.DiscountedAmount = order.TotalAmount * discount;
            Console.WriteLine($"Applied {((1 - discount) * 100)}% discount for {order.Customer.MembershipType} member. Total now: {order.DiscountedAmount}");
        }
    }
}


Step 4. Configure and Run the Rule Engine


In your Main method, set up the rule repository, compile rules, create a session, and create the list of Customers (Normal,Silver,Gold,MVP).

// See https://aka.ms/new-console-template for more information
// Load rules
using NRules;
using NRules.Fluent;
using RuleEngine_ShoppingCartDiscount;
using RuleEngine_ShoppingCartDiscount.Models;

var repository = new RuleRepository();
repository.Load(x => x.From(typeof(MembershipDiscountRule).Assembly));

// Compile rules
var factory = repository.Compile();

// Create a session
var session = factory.CreateSession();

var customers = new List<Customer>
{
    new Customer { MembershipType = "Normal" },
    new Customer { MembershipType = "Silver" },
    new Customer { MembershipType = "Gold" },
    new Customer { MembershipType = "VIP" }
};

// Create customer and order
foreach (var customer in customers)
{
    var order = new Order { Customer = customer, TotalAmount = 100 };

    // Insert facts into rules engine's memory
    session.Insert(order);

    // Start match/resolve/act cycle
    session.Fire();

    Console.WriteLine($"Final amount to pay: {order.DiscountedAmount}\n");
}

Console.ReadLine();


Step 5. Test the Application

Run the console application and it will print the All membership-based discount price.

Code Explanation

  • Rules Definition: A single rule handles all membership types by using a switch expression to determine the discount rate based on the membership type.
  • Rule Engine Setup: Rules are loaded, and compiled, and a session is created where the order is inserted as a fact.
  • Execution: The session.Fire() method triggers the rule engine, which evaluates the inserted facts against the compiled rules and applies the appropriate discount.

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Docker and Kubernetes: Containerizing React JS and.NET Core Applications

clock September 24, 2024 07:54 by author Peter

This tutorial will use React JS web forms and the.NET Core Web API to develop a prototype product application backend. Additionally, we will use Docker and Kubernetes to containerize the same process.

Example of a Product Application: Web API for Backend (.NET Core)
First, create a new Web API for Product Management using.NET Core.

Step 2: Install the NuGet packages listed below, which are for the in-memory database.

Step 3. Add the product class inside the entities folder.
namespace ProductManagementAPI.Entities
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

Step 4. Create an AppDbContext class inside the data folder with an in-memory connection and a DB set property.
using Microsoft.EntityFrameworkCore;
using ProductManagementAPI.Entities;
namespace ProductManagementAPI.Data
{
    public class AppDbContext : DbContext
    {
        public DbSet<Product> Products { get; set; }

        public AppDbContext(DbContextOptions<AppDbContext> options)
            : base(options)
        {
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // This check prevents configuring the DbContext if options are already provided
            if (!optionsBuilder.IsConfigured)
            {
                // Configure the in-memory database here, if needed
                optionsBuilder.UseInMemoryDatabase("InMemoryDb");
            }
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Optionally configure entity mappings here
        }
    }
}


Step 5. Add a product repository inside the repositories folder.
IProductRepository
using ProductManagementAPI.Entities;
namespace ProductManagementAPI.Repositories
{
    public interface IProductRepository
    {
        Task<List<Product>> GetAllProductsAsync();
        Task<Product> GetProductByIdAsync(int id);
        Task AddProductAsync(Product product);
        Task UpdateProductAsync(Product product);
        Task DeleteProductAsync(int id);
    }
}

ProductRepository
using Microsoft.EntityFrameworkCore;
using ProductManagementAPI.Data;
using ProductManagementAPI.Entities;
namespace ProductManagementAPI.Repositories
{
    public class ProductRepository : IProductRepository
    {
        private readonly AppDbContext _context;
        public ProductRepository(AppDbContext context)
        {
            _context = context;
        }
        public async Task<List<Product>> GetAllProductsAsync()
        {
            return await _context.Products.ToListAsync();
        }
        public async Task<Product> GetProductByIdAsync(int id)
        {
            return await _context.Products
                .AsNoTracking()
                .FirstOrDefaultAsync(p => p.Id == id);
        }
        public async Task AddProductAsync(Product product)
        {
            if (product == null)
            {
                throw new ArgumentNullException(nameof(product));
            }

            _context.Products.Add(product);
            await _context.SaveChangesAsync();
        }
        public async Task UpdateProductAsync(Product product)
        {
            if (product == null)
            {
                throw new ArgumentNullException(nameof(product));
            }

            _context.Entry(product).State = EntityState.Modified;
            await _context.SaveChangesAsync();
        }
        public async Task DeleteProductAsync(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                throw new KeyNotFoundException("Product not found.");
            }
            _context.Products.Remove(product);
            await _context.SaveChangesAsync();
        }

    }
}

Step 6. Create a new product controller with different action methods that we used to perform different operations using our front-end application after invoking the same.
using Microsoft.AspNetCore.Mvc;
using ProductManagementAPI.Entities;
using ProductManagementAPI.Repositories;
namespace ProductManagementAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        private readonly IProductRepository _repository;
        public ProductsController(IProductRepository repository)
        {
            _repository = repository;
        }
        [HttpGet]
        public async Task<IActionResult> GetAllProducts()
        {
            var products = await _repository.GetAllProductsAsync();
            return Ok(products); // Returns only the list of products
        }
        [HttpGet("{id}")]
        public async Task<IActionResult> GetProductById(int id)
        {
            var product = await _repository.GetProductByIdAsync(id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product); // Returns only the product data
        }
        [HttpPost]
        public async Task<IActionResult> AddProduct([FromBody] Product product)
        {
            if (product == null)
            {
                return BadRequest();
            }
            await _repository.AddProductAsync(product);
            return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);
        }
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateProduct(int id, [FromBody] Product product)
        {
            if (product == null || id != product.Id)
            {
                return BadRequest();
            }
            await _repository.UpdateProductAsync(product);
            return NoContent();
        }
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            await _repository.DeleteProductAsync(id);
            return NoContent();
        }
    }

}

Step 7. Register our services inside the service container and configure the middleware.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using ProductManagementAPI.Data;
using ProductManagementAPI.Repositories;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddDbContext<AppDbContext>();
builder.Services.AddCors(options => {
    options.AddPolicy("CORSPolicy", builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});

// Configure in-memory database
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseInMemoryDatabase("InMemoryDb"));

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseCors("CORSPolicy");
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 8. Finally, run the application and use Swagger UI to execute different API endpoints.

Sample Product Application: Frontend (React JS)

Let’s create a client application using React JS and consume the above API endpoints within it.

Step 1. Create a new React JS application with the help of the following command.
npx create-react-app react-netcore-crud-app

Step 2. Navigate to your project directory.
cd react-netcore-crud-app

Step 3. Install Axios to consume and hit backend API and bootstrap for designing purposes.
npm install axios
npm install bootstrap


npm install axios
npm install bootstrap


Step 4. Add the following components and services.
Product list component
// src/components/ProductList/ProductList.js
import React, { useState, useEffect } from 'react';
import ProductListItem from './ProductListItem';
import productService from '../../services/productService';
const ProductList = () => {
    const [products, setProducts] = useState([]);
    useEffect(() => {
        fetchProducts();
    }, []);
    const fetchProducts = async () => {
        try {
            const productsData = await productService.getAllProducts();
            setProducts(productsData);
        } catch (error) {
            console.error('Error fetching products:', error);
        }
    };
    const handleDelete = async (id) => {
        try {
            await productService.deleteProduct(id);
            fetchProducts(); // Refresh product list
        } catch (error) {
            console.error('Error deleting product:', error);
        }
    };
    const handleEdit = () => {
        fetchProducts(); // Refresh product list after editing
    };
    return (
        <div className="container">
            <h2 className="my-4">Product List</h2>
            <ul className="list-group">
    {Array.isArray(products) && products.length > 0 ? (
        products.map(product => (
            <ProductListItem key={product.id} product={product} onDelete={() => handleDelete(product.id)} onEdit={handleEdit} />
        ))
    ) : (
        <p>No products available</p>
    )}
</ul>
        </div>
    );
};
export default ProductList;


Product list item component

// src/components/ProductList/ProductListItem.js
import React, { useState } from 'react';
import productService from '../../services/productService';
const ProductListItem = ({ product, onDelete, onEdit }) => {
    const [isEditing, setIsEditing] = useState(false);
    const [editedName, setEditedName] = useState(product.name);
    const [editedPrice, setEditedPrice] = useState(product.price);
    const handleEdit = async () => {
        setIsEditing(true);
    };
    const handleSave = async () => {
        const editedProduct = { ...product, name: editedName, price: parseFloat(editedPrice) };
        try {
            await productService.updateProduct(product.id, editedProduct);
            setIsEditing(false);
            onEdit(); // Refresh product list
        } catch (error) {
            console.error('Error updating product:', error);
        }
    };
    const handleCancel = () => {
        setIsEditing(false);
        // Reset edited values
        setEditedName(product.name);
        setEditedPrice(product.price);
    };
    return (
        <li className="list-group-item">
            {isEditing ? (
                <div className="row">
                    <div className="col">
                        <input type="text" className="form-control" value={editedName} onChange={e => setEditedName(e.target.value)} required />
                    </div>
                    <div className="col">
                        <input type="number" className="form-control" value={editedPrice} onChange={e => setEditedPrice(e.target.value)} required />
                    </div>
                    <div className="col-auto">
                        <button className="btn btn-success me-2" onClick={handleSave}>Save</button>
                        <button className="btn btn-secondary" onClick={handleCancel}>Cancel</button>
                    </div>
                </div>
            ) : (
                <div className="d-flex justify-content-between align-items-center">
                    <span>{product.name} - ${product.price}</span>
                    <div>
                        <button className="btn btn-danger me-2" onClick={onDelete}>Delete</button>
                        <button className="btn btn-primary" onClick={handleEdit}>Edit</button>
                    </div>
                </div>
            )}
        </li>
    );
};
export default ProductListItem;


Product service
// src/services/productService.js
import axios from 'axios';
const baseURL = 'https://localhost:7202/api/products';
const productService = {
    getAllProducts: async () => {
        try {
            const response = await axios.get(
              baseURL,
              {
                timeout: 3000,
                headers: {
                  Accept: 'application/json',
                },
              },
            );

            return response.data;
          } catch (err) {
            if (err.code === 'ECONNABORTED') {
              console.log('The request timed out.');
            } else {
              console.log(err);
            }
          }
    },
    addProduct: async (product) => {
        const response = await axios.post(baseURL, product);
        return response.data;
    },
    deleteProduct: async (id) => {
        const response = await axios.delete(`${baseURL}/${id}`);
        return response.data;
    },
    updateProduct: async (id, product) => {
        const response = await axios.put(`${baseURL}/${id}`, product);
        return response.data;
    }
};
export default productService;


App component
// src/App.js
import React, { useState } from 'react';
import ProductList from './components/ProductList/ProductList';
import ProductForm from './components/ProductForm/ProductForm';
function App() {
    const [refresh, setRefresh] = useState(false);
    const handleProductAdded = () => {
        setRefresh(!refresh); // Toggle refresh state to trigger re-render
    };
    return (
        <div>
            <ProductList key={refresh} />
            <ProductForm onProductAdded={handleProductAdded} />
        </div>
    );
}
export default App;


Step 5. Run the application using the following command and perform the different CRUD operations with the help of the same.

Docker Files for Application
Docker file for backend application (.NET Core).
# Use the official .NET Core SDK as a parent image
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build

WORKDIR /app

# Copy the project file and restore any dependencies (use .csproj for the project name)
COPY *.csproj ./
RUN dotnet restore

# Copy the rest of the application code
COPY . .

# Publish the application
RUN dotnet publish -c Release -o out

# Build the runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime

WORKDIR /app
COPY --from=build /app/out ./

# Expose the port your application will run on
EXPOSE 80

# Start the application
ENTRYPOINT ["dotnet", "ProductManagementAPI.dll"]


  • Line 1-2: Uses the official .NET Core SDK image (mcr.microsoft.com/dotnet/sdk:6.0) as a base.
  • Line 4: Sets the working directory to /app.
  • Line 6-7: Copies the project file(s) (*.csproj) into the container.
  • Line 8: Runs dotnet restore to restore dependencies specified in the project file(s).
  • Line 10-11: Copies the rest of the application code into the container.
  • Line 13-14: Publishes the application in Release configuration (dotnet publish -c Release -o out), outputting to the out directory.
  • Line 16-17: Uses the official .NET Core ASP.NET runtime image (mcr.microsoft.com/dotnet/aspnet:6.0) as a base.
  • Line 19-20: Sets the working directory to /app and Copies the published output from the build stage (from /app/out) into the /app directory of the runtime stage.
  • Line 22-23: Exposes port 80 to allow external access to the application.
  • Line 25-26: Specifies dotnet ProductManagementAPI.dll as the entry point command to start the application.

Docker file for frontend application (React JS).
FROM node:16-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

  • Line 1: specifies the base image, using Node.js version 18.
  • Line 2: sets /app as the working directory for subsequent commands.
  • Line 3: Copies the contents of your local directory into the container's working directory.
  • Line 4: Installs the project dependencies inside the container.
  • Line 5: Build the production version of your React app.
  • Line 6: This exposes port 3000, which is where the application will run.
  • Line 7: The command starts the React application using serve to serve the build folder.

Next, modify your backend hard-coded URL in the product service.
// src/services/productService.js
import axios from 'axios';

//const baseURL = 'https://localhost:31912/api/products';
const baseURL = process.env.REACT_APP_API_URL;

const productService = {
    getAllProducts: async () => {
        try {
            const response = await axios.get(
              baseURL,
              {
                timeout: 3000,
                headers: {
                  Accept: 'application/json',
                },
              },
            );

            return response.data;
          } catch (err) {
            if (err.code === 'ECONNABORTED') {
              console.log('The request timed out.');
            } else {
              console.log(err);
            }
          }
    },
    addProduct: async (product) => {
        const response = await axios.post(baseURL, product);
        return response.data;
    },
    deleteProduct: async (id) => {
        const response = await axios.delete(`${baseURL}/${id}`);
        return response.data;
    },
    updateProduct: async (id, product) => {
        const response = await axios.put(`${baseURL}/${id}`, product);
        return response.data;
    }
};

export default productService;

Containerize the front-end and back-end application
Step 1. Build the docker images
docker build -t productbackendapp:latest .

docker build -t productfrontendapp:latest .

Step 2. Create a deployment and service YAML files for the backend application.

deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-management-api
  labels:
    app: product-management-api
spec:
  replicas: 1
  selector:
    matchLabels:
      app: product-management-api
  template:
    metadata:
      labels:
        app: product-management-api
    spec:
      containers:
      - name: product-management-api
        image: productbackendapp:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 80

service.yml
apiVersion: v1
kind: Service
metadata:
  name: product-management-api-service
  labels:
    app: product-management-api
spec:
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  selector:
    app: product-management-api


Step 3. Create deployment, service, and backend-config map YAML files for the frontend application.

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: react-client-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: react-client
  template:
    metadata:
      labels:
        app: react-client
    spec:
      containers:
      - name: react-client
        image: productfrontendapp:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 3000
        env:
        - name: REACT_APP_API_URL
          valueFrom:
            configMapKeyRef:
              name: backend-config
              key: REACT_APP_API_URL


service.yml
apiVersion: v1
kind: Service
metadata:
  name: react-client-service
spec:
  selector:
    app: react-client
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: NodePort


backend-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: backend-config
data:
  REACT_APP_API_URL: "http://localhost:30191/api/products"

Step 4. Apply all the above files one by one using kubectl commands. (Note: make sure Kubernetes is running on your system with docker daemon.)
kubectl apply -f deployment.yml
kubectl apply -f service.yml
kubectl apply -f backend-configmap.yml


Step 5. Verify the deployment, services, pods, and config map are up and running or not with the help of kubectl commands.

Step 6. Hit the backend and frontend product application using services.

 

Conclusion
In this article, we created a product management backend application using .NET Core with different API endpoints that are required to perform CRUD operations. Later on, we created the front-end application using React JS and consumed the back-end application inside the same with the help of Axios. Also, we containerized both applications with the help of Docker and Kubernetes.

HostForLIFE ASP.NET Core 9.0 Hosting

European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Month List

Tag cloud

Sign in