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 :: Caching Strategies in Angular and .NET Core

clock June 25, 2024 08:07 by author Peter

One essential tactic for enhancing the scalability and performance of web applications is caching. Caching can be done in a variety of methods inside the frameworks of Angular (a client-side framework) and.NET Core (a server-side framework), each appropriate for a particular set of circumstances. The following are some typical caching techniques for both:

Angular Caching
Caching HTTP Requests with an HTTP Interceptor

  • To cache HTTP responses, use an HTTP interceptor. By delivering cached responses when appropriate, this can assist lower the amount of HTTP queries made to the server.
  • For instance. Construct an HTTP interceptor that looks through the cache before sending a network request, and a caching service that saves responses.
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
  private cache = new Map<string, any>();
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.method !== 'GET') {
      return next.handle(req);
    }
    const cachedResponse = this.cache.get(req.url);
    if (cachedResponse) {
      return of(cachedResponse);
    }
    return next.handle(req).pipe(
      tap(event => {
        this.cache.set(req.url, event);
      })
    );
  }
}


Service Worker for Offline Caching
  • Use Angular's built-in service worker support to cache assets and API responses for offline access and faster load times.
  • Example. Enable the Angular service worker by running ng add @angular/pwa and configure caching in the ngsw-config.json file.
Local Storage or IndexedDB
Store data in the browser's local storage or IndexedDB for long-term caching. This can be useful for storing user preferences or large datasets that rarely change.
Example. Use Angular's localStorage or IndexedDB wrappers to store and retrieve data.
localStorage.setItem('key', JSON.stringify(data));
const cachedData = JSON.parse(localStorage.getItem('key'));

Caching in .NET Core
In-Memory Caching
Store cache data in the memory of the web server. This is useful for small amounts of data that change frequently.
Example. Use the IMemoryCache interface to cache data in memory.
public class MyService
{
    private readonly IMemoryCache _memoryCache;
    public MyService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }
    public string GetData()
    {
        string cacheKey = "myCacheKey";
        if (!_memoryCache.TryGetValue(cacheKey, out string cachedData))
        {
            cachedData = "Data from database";
            _memoryCache.Set(cacheKey, cachedData, TimeSpan.FromMinutes(5));
        }
        return cachedData;
    }
}


Distributed Caching
  • Store cache data in a distributed cache like Redis or SQL Server. This is useful for data that needs to be shared across multiple servers.
  • Example. Use the IDistributedCache interface to cache data in Redis.
public class MyService
    {
        private readonly IDistributedCache _distributedCache;
        public MyService(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }
        public async Task<string> GetDataAsync()
        {
            string cacheKey = "myCacheKey";
            var cachedData = await _distributedCache.GetStringAsync(cacheKey);
            if (cachedData == null)
            {
                cachedData = "Data from database";
                await _distributedCache.SetStringAsync(cacheKey, cachedData, new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
                });
            }
            return cachedData;
        }
    }

Response Caching Middleware
  • Cache HTTP responses at the server level to improve performance for subsequent requests.
  • Example. Configure response caching middleware in the Startup class.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCaching();
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseResponseCaching();
        app.Use(async (context, next) =>
        {
            context.Response.GetTypedHeaders().CacheControl =
                new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
                {
                    Public = true,
                    MaxAge = TimeSpan.FromSeconds(60)
                };
            await next();
        });
    }


Combining Angular and .NET Core Caching

To achieve optimal performance, you can combine caching strategies in Angular and .NET Core:

Client-Side Caching

Cache static assets and API responses in Angular using service workers or local storage.
Implement HTTP interceptors to cache responses and reduce server requests.

Server-Side Caching
Use in-memory or distributed caching in .NET Core to store frequently accessed data.
Implement response caching middleware to cache entire HTTP responses.

Conclusion
You can build a strong and effective caching strategy that greatly improves the efficiency and scalability of your Angular and.NET Core applications by utilizing both client-side and server-side caching.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: How to Exploring the Experimental Attribute in C#?

clock June 20, 2024 13:52 by author Peter

In software development, the introduction of new features often involves a period of testing and feedback. During this period, developers need a way to indicate that certain features are not fully mature and may be subject to change. The Experimental attribute in C# serves this purpose by marking classes, methods, or other members as experimental. This blog will delve into what the Experimental attribute is, why it's useful, how to implement it, and provide detailed code snippets to illustrate its usage.


What is the Experimental Attribute?

The Experimental attribute is a custom attribute that can be applied to various members of your codebase (e.g., classes, methods, properties). It indicates that the marked feature is experimental, meaning it's in a testing phase and may undergo significant changes or might even be removed in future releases.
Why Do We Need It?

  • Communication: It communicates to other developers that the marked feature is not yet stable and should be used with caution.
  • Encapsulation: It helps encapsulate features that are still under development, making it easier to manage their lifecycle.
  • Documentation: It serves as a form of documentation, indicating the experimental nature directly in the code.


Implementing the Experimental Attribute
Step 1. Define the Experimental Attribute
First, you need to define the Experimental attribute. This is a simple custom attribute class.
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)]
public class ExperimentalAttribute : Attribute
{
    public string Message { get; }
    public ExperimentalAttribute(string message)
    {
        Message = message;
    }
}


Step 2. Applying the Experimental Attribute
You can apply the Experimental attribute to various parts of your codebase to indicate that they are experimental.
using System;
[Experimental("This class is experimental and may change in the future.")]
public class ExperimentalFeature
{
    [Experimental("This method is experimental and may change in the future.")]
    public void ExperimentalMethod()
    {
        Console.WriteLine("This is an experimental method.");
    }
    [Experimental("This property is experimental and may change in the future.")]
    public string ExperimentalProperty { get; set; }
}

Step 3. Using the Experimental Features
When using the experimental features, it’s important to handle them with care, knowing that their behavior might change.
public class Program
{
    public static void Main()
    {
        ExperimentalFeature feature = new ExperimentalFeature();
        feature.ExperimentalMethod();
        feature.ExperimentalProperty = "Testing";
        Console.WriteLine(feature.ExperimentalProperty);
    }
}


Detailed Explanation of Code Snippets
Defining the Attribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)]
public class ExperimentalAttribute : Attribute
{
    public string Message { get; }
    public ExperimentalAttribute(string message)
    {
        Message = message;
    }
}

  • [AttributeUsage]: Specifies the program elements to which the attribute can be applied. Here, it can be applied to classes, methods, and properties.
  • Constructor: Initializes the attribute with a message that provides additional context or warnings about the experimental feature.

Applying the Attribute
[Experimental("This class is experimental and may change in the future.")]
public class ExperimentalFeature
{
    [Experimental("This method is experimental and may change in the future.")]
    public void ExperimentalMethod()
    {
        Console.WriteLine("This is an experimental method.");
    }
    [Experimental("This property is experimental and may change in the future.")]
    public string ExperimentalProperty { get; set; }
}

  • Class Level: Marks the entire class as experimental.
  • Method Level: Marks a specific method within the class as experimental.
  • Property Level: Marks a property within the class as experimental.

Using the Experimental Features
public class Program
{
    public static void Main()
    {
        ExperimentalFeature feature = new ExperimentalFeature();
        feature.ExperimentalMethod();
        feature.ExperimentalProperty = "Testing";
        Console.WriteLine(feature.ExperimentalProperty);
    }
}


Creating an Instance: An instance of the ExperimentalFeature class is created and its experimental method and property are used. This is straightforward, but developers should be aware of the experimental status.

Conclusion

The Experimental attribute in C# is a powerful tool for marking features as experimental, providing clear communication to other developers about the stability and maturity of the code. By defining and applying this custom attribute, you can better manage the development and evolution of new features, ensuring that everyone involved in the project is aware of their experimental nature.

Using the Experimental attribute helps in documenting and encapsulating the experimental features effectively, making your codebase more maintainable and understandable. While the examples provided are simple, the approach can be scaled to larger and more complex projects, enhancing the overall development process.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: How to Frozen Collections in .NET 8?

clock June 11, 2024 10:38 by author Peter

With the release of .NET 8, Microsoft has introduced several new features and improvements to enhance performance and developer productivity. Among these, one of the most notable additions is the concept of "Frozen Collections." This article explores what frozen collections are, their benefits, and how to use them effectively in your .NET applications.

Frozen Collections: What Are They?
A new kind of immutable collection called frozen collections is intended to improve access speed and reduce memory usage in order to maximize performance. Frozen collections are intentionally made to be immutable from the beginning, in contrast to conventional read-only collections, which are frequently nested inside changeable collections. Once a collection is frozen, its immutability ensures that it cannot be altered, which has various performance benefits.

Key Benefits of Frozen Collections
1. Performance Optimization
The primary advantage of frozen collections lies in their performance. By guaranteeing immutability, the .NET runtime can make several optimizations.

  • Memory Efficiency: Since frozen collections do not change, they can be stored in a compact form, reducing the overall memory footprint.
  • Access Speed: Lookup operations are faster because the underlying data structure can be optimized for read access.
  • Thread Safety: Frozen collections are inherently thread-safe, eliminating the need for synchronization mechanisms when accessing them concurrently.


2. Predictable Behavior
Immutable collections provide predictable behavior, which simplifies debugging and reasoning about code. When a collection is guaranteed not to change, developers can avoid common pitfalls related to concurrent modifications and side effects.

3. Simplified Code

Using frozen collections can lead to cleaner and more maintainable code. Since the collections cannot be altered, functions that operate on them do not need to account for modifications, leading to simpler and more readable implementations.

Using Frozen Collections in .NET 8
To use frozen collections in .NET 8, you can leverage the new FrozenCollection<T> class available in the System.Collections.Immutable namespace. Here’s a basic example to illustrate how to create and use a frozen collection.
using System;
using System.Collections.Immutable;

class Program
{
    static void Main()
    {
        var mutableList = new List<int> { 1, 2, 3, 4, 5 };

        // Create a frozen collection from the mutable list
        var frozenCollection = mutableList.ToFrozenCollection();

        // Attempting to modify the frozen collection will result in a compile-time error
        // frozenCollection.Add(6); // Not allowed

        // Accessing elements is fast and efficient
        Console.WriteLine(frozenCollection[0]); // Output: 1

        // The collection is inherently thread-safe
        Parallel.ForEach(frozenCollection, item => {
            Console.WriteLine(item);
        });
    }
}

public static class Extensions
{
    public static FrozenCollection<T> ToFrozenCollection<T>(this IEnumerable<T> source)
    {
        var builder = new FrozenCollectionBuilder<T>();
        foreach (var item in source)
        {
            builder.Add(item);
        }
        return builder.ToFrozenCollection();
    }
}

Creating a Frozen Collection
In the example above, we start with a mutable list of integers. Using an extension method, we convert this list into a FrozenCollection<T>. The FrozenCollectionBuilder<T> class is used to add elements to the collection before it is frozen.

Accessing Elements

Once the collection is frozen, we can access its elements just like a regular collection. However, any attempt to modify the collection will result in a compile-time error, ensuring immutability.

Thread Safety

The inherent thread safety of frozen collections allows for safe concurrent access without additional synchronization, making them ideal for use in multi-threaded environments.

Conclusion

Frozen collections in .NET 8 represent a significant advancement in collection management, providing developers with powerful tools to create high-performance, immutable data structures. By leveraging these collections, you can achieve better memory efficiency, faster access times, and simplified code. As .NET continues to evolve, features like frozen collections will play a crucial role in enabling developers to build robust and efficient applications.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Increasing Code Quality: The Best Methods for Successful Code Reviews

clock June 3, 2024 07:40 by author Peter

Clarify goals and expectations prior to starting a code review. Make sure that the review's goal is understood, whether it is to verify code quality, find errors, impart knowledge, or uphold coding standards. Establishing specific goals guarantees that reviewers concentrate on the most pertinent parts of the code and speeds up the review process.

Specify the codes
Specify recommended practices and coding standards that all developers should follow. This covers standards for code organization, formatting rules, naming conventions, and documentation. Adhering to coding standards consistently makes the codebase easier to read, maintain, and scale, which in turn makes it easier for developers to communicate and work together on code.

Conduct regular reviews
Regularly incorporate code reviews into the development process. Establish systems that enable peer reviews for each code update, such as pull requests or code review software. Frequent reviews guarantee that code quality is maintained at a high level throughout the development process, assist find problems early, and prevent the building of technical debt.

Keep reviews small and focused

Divide code modifications into more digestible, smaller pieces to enable targeted and effective assessments. Big code changes are frequently intimidating and prone to problems that go unnoticed. Smaller, more targeted evaluations allow reviewers to give more insightful criticism and developers to handle issues more skillfully.

Foster a collaborative culture

Encourage a cooperative and positive atmosphere when conducting code reviews. Urge reviewers to focus on the code rather than the developer and to offer constructive criticism in a courteous manner. Promote transparent communication, stimulate dialogue, and take use of evaluations as a means of imparting information and providing mentorship.

Automate where possible
Automate repetitive tasks and checks to streamline the code review process. Use automated tools for static code analysis, code formatting, and unit testing to identify common issues and enforce coding standards automatically. Automation reduces manual effort, improves consistency, and frees up time for reviewers to focus on higher-level concerns.

Prioritize feedback and follow-up

Prioritize feedback based on its severity, impact, and urgency. Address critical issues and blockers promptly to prevent delays in the development process. Keep track of feedback and action items, follow up on unresolved issues, and ensure that all feedback is addressed satisfactorily before closing the review.

Encourage learning and growth
View code reviews as opportunities for learning and professional growth. Encourage developers to seek feedback, ask questions, and engage in discussions to deepen their understanding of coding principles and best practices. Provide constructive feedback, mentorship, and guidance to help developers improve their skills and grow as professionals.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Using and Exercising the String Builder in.NET C#

clock May 27, 2024 07:04 by author Peter

Once a string type is generated in C#, its contents cannot be changed since the string type is immutable. This suggests that modifying a string-type object after it has been created will cause the object to be constructed again in memory as a new instance. Additionally, changing the string frequently could cause performance problems.


StringBuilder solves this problem because, unlike string, it can dynamically increase its memory to support any kind of manipulation.

How to make a StringBuilder correctly?
It is not too difficult to instantiate a StringBuilder. We can instantiate a class by using the new keyword to create an instance of that class. A StringBuilder also contains other constructors.

Conversion of StringBuilder to String
We can use a StringBuilder for any type of string manipulation. But the StringBuilder does not return a string. Therefore, in order to retrieve a string, we must use the ToString() method.
var sb = new StringBuilder("Peter Scott");

var name = sb.ToString();

The content is first converted to a string and then a StringBuilder is created with a default text.

StringBuilder Techniques

We can work with a StringBuilder's contents in a few different ways. These include Replace(), Insert(), Clean(), Remove(), Append(), AppendLine(), and AppendFormat().

Append
A new string is appended to the end of the existing StringBuilder using the Append() function. The StringBuilder's length can be doubled, and space allocation happens automatically.
var sb = new StringBuilder("Hello, ");
sb.Append("Peter Scott");
var name = sb.ToString();

Console.WriteLine("{0} - {1}", nameof(name), name);

AppendLine
AppendLine() is useful when we want to add a line terminator to our StringBuilder. To accomplish this, we can make a StringBuilder and use the following method.


var sb = new StringBuilder("Hello, ");
sb.Append("Peter Scott");
sb.AppendLine();
sb.AppendLine("How are you?");

Console.WriteLine(sb.ToString());

AppendFormat
AppendFormat() appends a string to the end of StringBuilder in a predetermined format. The conventions of either the chosen culture or the current system culture are revealed in the produced string. The method lets us pass the desired format for our string as an input.
var sb = new StringBuilder("Hello, ");
sb.Append("Peter Scott");
var text = "C# Corner Rank";
var rank = 120;
sb.AppendLine();
sb.AppendFormat("{0} - {1}", text, rank);

Console.WriteLine(sb.ToString());


This method adds, to our StringBuilder object, a string, substring, character array, part of a character array, or the string representation of a primitive data type at a certain place. We may also add the index for our insertion using this method.
var sb = new StringBuilder("Hello");
var name = ", Peter Scott";
sb.Insert(5, name);

Console.WriteLine(sb.ToString());


Replace
Several character occurrences in the StringBuilder object are replaced by this method. The desired character sequence and a new value are entered into the method as inputs.
var sb = new StringBuilder("Hello");
var name = ", Peter Scott";
sb.Insert(5, name);
Console.WriteLine("Orioginal data: {0}", sb.ToString());
Console.WriteLine();

sb.Replace("Lee", "Cooper");

Console.WriteLine("Replace data: {0}", sb.ToString());


Replace
A predetermined amount of characters are removed from the StringBuilder using the Remove() function. The start index and the number of characters to be deleted are the input parameters.

var sb = new StringBuilder("Hello");
sb.Append(", Peter Scott");

Console.WriteLine("Orioginal data: {0}", sb.ToString());
Console.WriteLine();

sb.Remove(0, 7);

Console.WriteLine("Remove data: {0}", sb.ToString());


Remove
The Remove technique eliminates 7 characters starting at index 0.

Clear

The StringBuilder object's characters are all eliminated using this function. This method reduces the length of a StringBuilder to zero.
var sb = new StringBuilder("Hello");
sb.Append(", Peter Scott");

Console.WriteLine("Orioginal data: {0}", sb.ToString());
Console.WriteLine();

sb.Clear();

Console.WriteLine("Clear data: {0}", sb.ToString());


Clear
We learned the new technique and evolved together.
Happy coding!



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: The Issue of Thread Synchronization and It's ResolutionAn Alternative View on Stack Memory in.NET

clock May 20, 2024 09:40 by author Peter

We shall discuss the Stack memory in.NET in this article. The portion of memory in.NET that is assigned to a thread is called the Stack. To put it simply, each thread has a stack—that is, its own memory—that is the last in, first out. Note: Because there is a connection between Stack memory and Heap memory, some parts of the article are explained but do not include Heap memory.


Memory Stack
Computer memory architecture is where the idea of a memory stack first appeared. Its essence also affects the various programming languages. After all, in any computer system, a programming language is a form of abstraction or communication. Many programming languages are forced to adopt it as a result.
So, even the CPU has push-and-pop instructions for providing thread (stack) execution and other things. Well, without further confusion, let’s continue our look at .NET and their use of Stack memory.

Nearly all developers are aware that a data structure is referred to as a "stack." There are two general strategies for the stack data structure: FIFO and LIFO. We employ the LIFO (last in first out) method to operate our stack memory. Moreover, distinguish between stack memory and the stack data structure. Because stack memory makes use of the stack data structure technology, names are the same. There are two different kinds of memory in.NET; a heap and a stack, both of which are found in RAM. Method calls, return addresses, arguments/parameters, and local variables are all tracked by the stack. However, reference-type objects are stored in Heap (this is a topic for another article).

To put it simply, every thread has a memory location of its own. This is the stack, which represents each thread's local memory. Thus, during method execution, threads define a local variable, retrieve the result, and then call another method, and so on. It is these actions that require stack memory. Furthermore take note that just one thread of execution uses the stack memory. Conversely, a heap is shared memory used by multiple threads, the extent of which is specified by the operating system process.

Allocation of Stacks
Stack allocation, to put it simply, is a memory allocation technique that makes use of the stack, a section of memory. The most recently allocated memory is the first to be deallocated because, as I previously explained, the stack uses the LIFO (last in first out) method of operation. There are two types in.NET. A reference type is one, and a value type is the other. Thus, the value of the type is saved immediately when allocating a value type in a stack. However, when dealing with reference types that reside in heaps, it stores the heap's reference address, also known as the object's pointer. Generally speaking, it means that a stack only contains values (a reference address is a type of value, but we'll talk about that in another article).

The figure above illustrates how two declared variables are present in a separate stack frame within the thread stack: a flag (int32, value type) and a pInst, instance of the Person class. We won't talk about the heap, but the picture demonstrates that it has the flag's value of 1, as well as the reference address of pInst. The address for the stack that refers the heap object—an object with data and methods stored on the heap—is what is meant to be used in the reference type. In .NET, Stack memory has a fixed size which a 1-MB stack is allocated when a thread is created. This size is predefined once the program starts, providing a consistent amount of stack memory available for function calls and local variable storage. However, the fixed nature of the stack means that it can only hold a limited amount of data. In addition, the speed of stack memory operations is a key advantage based on the simplicity of the stack’s push and pop mechanisms.

So, almost everyone wonders why the Stack memory is fast.

  • Firstly, stack memory uses static memory allocation, meaning that memory is given and taken away in a predictable way. This removes the need for complicated memory management, making operations quick and efficient.
  • Secondly, as I said above the stack has a fixed size, which avoids the overhead of finding available memory space, unlike heap memory. The simple push-and-pop operations also make it faster. These operations just adjust the stack pointer, which is very fast compared to the more complex operations needed for heap memory.
  • Thirdly, modern CPUs have special instructions for stack operations, like pushing and popping data, which are made to be very fast. The CPU also uses a dedicated register, the stack pointer, to efficiently manage the stack’s top position. In addition, stack memory often gets loaded into the CPU cache. This happens because stack data is frequently accessed in a straight line, making it more friendly to the cache. In contrast, accessing heap memory is indirect and can involve more cache misses, making it slower. This direct hardware support and cache efficiency ensure that stack operations have very little overhead.

Stack Frame/Call Stack

A stack frame, also known as an activation record, is a section of the stack memory allocated for a single function/method call. When a function/method is called, a new stack frame is allocated, and when the function/method returns, its stack frame is deallocated.

Basically, a stack frame contains.

  • Function/Method Arguments: The values passed to the function/method.
  • Local Variables: Variables that are declared inside the function/method.
  • Return Address: The location in the code to return to after the function/method completes.


Let’s start the explanation with a code example.
public void Calculate()
{
    int a = 1;
    int b = 10;
    int c = a + b;

    Write(c);
    Console.WriteLine("Finished");
}

public void Write(int number)
{
    Console.WriteLine(number);
}


As you can see, there is a method named Calculate that declares 3 variables and is also called the Write method it.

Assume that, a thread calls the Calculate method, how would this be visualized?

As you can see from the image above, the first stack frame belongs to the Calculate method which stores 3 variables, one of which is the result of the sum of two variables. The second stack frame belongs to the Write method which stores the argument and return address (line 9 just for understanding, in fact, it contains different values).

So when the Write method completes the execution, the second stack frame is deallocated and the thread returns to the first stack frame that belongs to the Calculate method, through the return address it will execute from that address line and complete the execution of the Calculate method. In this way, the call stack loop that was called in this thread will continue to execute (in our case, execution is completed).

StackOverflowException

As I mentioned before, the stack size is limited, and for this reason, when any function/method is called in an unlimited and nested way, a “StackOverflowException” occurs.

Assume that we have some function/method that is using recursion but there is not any limitation. It means the method has some calculation in which there is no return state. So, in this case, we will get “StackOverFlowException”.

Let’s take a look at a code example.

public class Program
{
    public static void Main()
    {
        Calculate(2, 3);
    }

    public static void Calculate(int a, int b)
    {
        int c = a + b;
        Calculate(c, c * c);
    }
}


When you execute this code, you will get “StackOverFlowException” because recursion provides the call itself and there is no return state or limit in this function/method. With this technique, every call to the function/method creates new stack frames and when the stack limit is exceeded, we get a “StackOverFlowException”.

If you would like to learn more, stay tuned!



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: The Issue of Thread Synchronization and It's Resolution

clock May 14, 2024 06:53 by author Peter

Overview of synchronization issues

  • Assume that our shared count variable has a starting value of 0.
  • We wish to use this variable for two simultaneous activities.

Using Thread 1, add 1 to 100 to this variable.
Using Thread 2, subtract 1 to 100 from this value.

  • Print the variable's count at the end. It should ideally print 0. but it won't Because working on the same variable simultaneously by many threads can have unexpected outcomes.

When does the synchronization problem happen?
Critical Section
When more than one threads try to access the same code segment that segment is known as the critical section.
So, when more than one thread is there in the critical section at the same time, it can lead to unexpected results and synchronization problem.

Race Condition
If more than one thread tries to enter inside the critical section at the same time then it might lead to the synchronization problem.

Preemption
Preemption is the ability of the operating system to preempt(that is stop or pause) a currently scheduled task in favor of a higher priority task.
A program that is inside the critical section and CPU preempts then it can lead to the synchronization problem.

Solutions to the synchronization problem

In C#, there are several ways to synchronize access to shared resources to ensure thread safety and prevent race conditions.

Using lock keyword
The lock keyword provides a convenient way to create a synchronized block of code. It internally uses the Monitor class to achieve synchronization. The lock keyword ensures that only one thread can execute the locked code block at a time.

Example
private static object syncObject = new object();
private void Increment()
{
lock(syncObject)
{
    // Critical section: Access shared resource
}
}


Using Monitor Class
Instead of using the lock keyword, we can directly use methods of the Monitor class for synchronization.

Example
private static object syncObject = new object();
private void Increment()
{
Monitor.Enter(syncObject);
try
{
    // Critical section: Access shared resource
}
finally
{
    Monitor.Exit(syncObject);
}
}


Using Mutex
A mutex is a synchronization primitive that allows only one thread to acquire it at a time. It's typically used for inter-process synchronization to synchronize threads within the same process.

Example
private static Mutex mutex = new Mutex();
private void Increment()
{
mutex.WaitOne();
try
{
    // Critical section: Access shared resource
}
finally
{
    mutex.ReleaseMutex();
}
}

Using Semaphore
A semaphore is a synchronization primitive that allows a specified number of threads to enter a critical section simultaneously. It's useful when we want to limit the number of threads accessing a resource.

Example
private static Semaphore semaphore = new Semaphore(1, 1); // Limits access to one thread
private void Increment()
{
semaphore.WaitOne();
try
{
    // Critical section: Access shared resource
}
finally
{
    semaphore.Release();
}
}


Using Interlocked Class
The Interlocked class provides atomic operations for variables that are shared between threads. It's useful for performing simple operations like incrementing a counter without the need for locking.

Example
private int counter = 0;
public void Increment()
{
Interlocked.Increment(ref counter);
}

Properties of a good solution to the synchronization problem

Mutual Exclusion: Only one thread should be allowed inside the critical section at any point in time.
Progress: Overall system should keep on making progress. There shouldn't be a deadlock condition.
Bounded waiting: No thread should wait outside the critical section infinitely. There should be some bound on the waiting time.
No Busy Waiting: If a thread has to continuously check if they can enter inside the critical section or not is Busy Waiting.
    while(!allowed to enter critical section)

    {
        checking(); // <---- This is the busy waiting.
    }


There shouldn't be Busy waiting as it can have several consequences like

  • Inefficient use of CPU resources and wasted energy.
  • Reduced performance.
  • Increased power consumption.
  • Potential deadlocks and etc.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Comprehending.NET Core Garbage Collection

clock May 6, 2024 08:07 by author Peter

A key component of memory management in contemporary programming languages like C# is garbage collection (GC). The GC system is essential to.NET Core because it automatically recovers memory that is no longer in use, eliminating memory leaks and guaranteeing effective memory use. The purpose of this article is to examine the techniques and the parts that make up the garbage collection system in .NET Core.

Comprehending Trash Collection
Effective memory management is essential for developing strong applications, especially with C# and.NET Core. Garbage Collection (GC) is a key component of this ecosystem's automated memory management system, effectively managing memory deallocation and allocation. The process of automatically recovering memory used by objects that an application no longer needs is known as garbage collection. This is accomplished in.NET Core via a highly developed garbage collector that operates in the background, regularly searching the managed heap for things that have not been referenced and recovering their memory. Fundamentally, managed heap memory—the memory used by C# applications to store instantiated objects—is allocated and released by Garbage Collection in.NET Core.

Garbage Collection Components in.NET Core

  • Managed Heap: The managed heap is a section of memory set aside for the purpose of storing application-created objects by the Common Language Runtime (CLR). The managed heap in.NET Core is separated into three generations: Gen0, Gen1, and Gen2. Initially assigned to Gen0, objects are promoted to higher generations as long as they survive garbage collection cycles.
  • Garbage Collector: In.NET Core, the garbage collector is the main element in charge of memory reclamation. It runs in the background, periodically scanning the managed heap to locate and retrieve things that the application can no longer access. To maximize collection efficiency and reduce interference with application performance, the garbage collector employs a variety of algorithms and heuristics.
  • Finalization Queue: Finalization is supported by.NET Core, enabling objects to carry out cleanup operations prior to being picked up by garbage collectors. The finalization queue is a dedicated queue for objects that need to be finalized. In order to guarantee that their finalizers are called prior to their reclamation, objects in the finalization queue are handled independently during garbage collection.
  • Large Object Heap (LOH): In.NET Core, large objects (usually those greater than 85,000 bytes) are stored in the Large Object Heap, a separate section of the managed heap. Large items are treated differently by the trash collector due to their size in order to reduce fragmentation and enhance performance.

Three basic steps are involved in the operation of this automated process:

  • Marking: To determine which objects are still in use, the GC begins by iterating through all object references, beginning at the roots.
  • Relocating: The GC compacts the heap by moving active objects closer to one another after detecting them, improving speed and memory layout. It modifies references in parallel to take into account the updated memory addresses.
  • Clearing: The last phase involves the GC freeing up memory that has been occupied by objects that are no longer referenced, freeing up resources for new allocations.

Including Future Generations to Increase Efficiency
Generational memory management is one of the main techniques that.NET GC uses to increase efficiency. The managed heap is divided into three generations, each of which serves different object categories:

  • Gen 0: This segment accommodates short-lived objects, which typically have a transient lifespan within the application. As a result, a significant portion of memory reclamation occurs in this generation.
  • Gen 1: Positioned as a buffer between short-lived and long-lived objects, Generation 1 serves to segregate objects based on their longevity. Objects surviving multiple garbage collections in Gen 0 are promoted to Gen 1.
  • Gen 2: Comprising long-lived objects, Generation 2 hosts entities expected to persist throughout the application's lifecycle. Garbage collections within this segment are less frequent due to the enduring nature of its occupants.

Conclusion

Within the.NET Core ecosystem, garbage collection is a fundamental component of memory management because it provides an automated solution to the challenges associated with memory allocation and deallocation. Through an understanding of GC's inner workings and an embrace of generational memory management, developers may create apps that are more resilient to changing workloads and perform better overall. The finalization queue, managed heap, garbage collector, and other parts of.NET Core integrate flawlessly to automate memory management and offer a dependable execution environment for.NET Core programs.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Using.NET Aspire App to Deploy.NET 8 Core Web API in the Cloud

clock May 2, 2024 07:43 by author Peter

A cloud-ready, opinionated stack for developing distributed, observable, production-ready applications is.NET Aspire. Delivery of.NET Aspire occurs via a group of NuGet packages that address particular cloud-native issues. Microservices, which are tiny, interconnected units of code, are frequently used in place of a single, large code base in cloud-native applications. Typically, cloud-native applications use a lot of resources, including messaging, databases, and caching.

Overview
A cloud-ready toolset for creating contemporary distributed applications is called.NET Aspire. Its main goal is to assist developers in building observable, scalable microservices. .NET Aspire makes it easier to deploy.NET Core APIs and enables you to rapidly create cloud-native applications with a set of NuGet packages. Rather to having a single, massive code base, these applications usually comprise of smaller, connected components. For their operations to be successful in a cloud setting, they frequently depend on services like messaging, databases, and caching.

Starting a New Visual Studio 2022 Project
Starting a New Visual Studio 2022 Project An overview Launch the Visual Studio application, then choose "Create a new project."

The chosen application's project structure will be created by Visual Studio 2022. Since we're utilizing the ASP.Net Core Web API in this example, we can either utilize the pre-existing controller or construct a new one to write the code. You can enter the code there and use it to create or run the program.

To create a .NET Aspire Application, follow these steps.

  • Right-click on the Solution section in Visual Studio.
  • Select "Add" > "New Project."
  • Choose "Create a new project."
  • Find and select ".NET Aspire Application" from the list of project templates.

The .NET Aspire Application is a basic starter template that includes only the AppHost and ServiceDefaults projects. This template is designed to only provide the essentials for you to build off of

The .NET Aspire Application is a basic starter template that includes only the AppHost and ServiceDefaults projects. This template is designed to provide the essentials to build upon.

Project Structure

  • WEB: This is an ASP.NET Core API project, This project depends on the shared AspireCloud.ServiceDefaults project.
  • AspireCloud.AppHost: This is an orchestrator project designed to connect and configure the different projects and services of your app. This project handles running all of the projects that are part of the .NET Aspire application. The orchestrator should be set as the Startup project, and it depends on the WEBThis is the project responsible for running the applications inside of this solution.
  • AspireCloud.ServiceDefaults: This is a.NET Aspire shared project to manage configurations that are reused across the projects in your solution related to resilience, service discovery, and telemetry. This project ensures that all dependent services share the same resilience, service discovery, and OpenTelemetry configuration.

In the Program.cs file on the AppHost project, you can see the following code.
var builder = DistributedApplication.CreateBuilder(args);
var RESTAPI = builder.AddProject<Projects.WEB>("RESTAPI");
builder.Build().Run();

WEB .NET core API program.cs file configured in Aspire.ServiceDefaults.
builder.AddServiceDefaults();

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle

builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSwaggerGen();

//cloud configuration:

builder.AddServiceDefaults();

var app = builder.Build();

// Configure the HTTP request pipeline.

if (app.Environment.IsDevelopment())

{
    app.UseSwagger();

    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

On the Extensions. cs file, you can find some extension methods such as:

  • AddServiceDefaults: used to add default functionality.
  • ConfigureOpenTelemetry: used to configure OpenTelemetry metrics and tracing
  • AddDefaultHealthChecks: adds default health checks endpoints
  • MapDefaultEndpoints: maps the health check endpoint to /health and the liveness endpoint to alive.

Dashboard
Start the project with AspireCloud.AppHost is the Starter project (this project knows how to run the whole distributed application), and the following page will be opened in your browser with this nice dashboard.

In this dashboard, you can monitor various parts of your app, such as,

  • Projects: Displays information about your .NET projects in your .NET Aspire app, such as the app state, endpoints, and environment variables.
  • Containers: Displays information about your app containers, such as state, image tag, and port number (you should also see the Redis container you added for output caching with the name you provided.
  • Executables: Displays the running executables used by your app.
  • Logs: In this section, you can see the output logs for the projects, containers, and executables, and can also see the logs in a table format (structured).
  • Traces: displays the traces for your application, providing information about the requests.
  • Metrics: Displays various instruments and meters that are exposed and their corresponding dimensions for your app.

The Traces for your APIs.

 



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Difference Between ReadOnly and Constant in .NET C#

clock April 29, 2024 09:48 by author Peter

Values that cannot be changed at runtime in C# are declared using the readonly and const keywords. However, there are several notable distinctions between these two words.

Constant: Describe It
Const is a compile-time constant, meaning that its value can only be set at compile time and cannot be modified by runtime. Usually, it is used for declaring global constants, such as mathematical constants or other values that aren't expected to change throughout program execution. Const values are intrinsically static and always belong to the class; they are not particular to any one instance of the class. Since they are by nature public, they can also be accessed from anywhere in the code.

public class Constants
{
  public const string CLIENT_ID = "ClientId";
}

ReadOnly:  What Is It?
Conversely, readonly fields are runtime constants, which means that only the class's constructor has the ability to modify them. Their value is predetermined at runtime. Since the readonly fields are instance-level variables, a duplicate of each field exists in each class instance. Additionally, they can be marked as static, indicating that they are part of the class itself rather than a specific class instance. You have to specifically declare the access level because the readonly fields are not inherently public.

public class TestService
{
  private readonly ISomeService _someService;

  public TestService(ISomeService someService)
  {
    _someService = someService;
  }
}


Key distinctions in C# between readonly and constant:
Constants in C# are defined using the readonly and const keywords, although their applications and capabilities differ.

When their values are set is one of the biggest distinctions between readonly and const. Values defined with the const keyword must be set at compile time and will not change at runtime. It implies that const values are decided upon during code compilation rather than execution. Conversely, readonly values are set at runtime and can only have their values assigned within the class constructor.

Const values are also fundamentally different since they are always implicitly static—that is, they are part of the class and not an instance of it. On the other hand, readonly values need to specifically state the access level and can be either instance-level or static.

The following example demonstrates the distinctions between readonly and const:
public class TestClass
{
    public const double PI = 3.14;
    public readonly string Name;

    public TestClass(string name)
    {
        Name = name;
    }
}


In this case, the const value PI was set to 3.14 at compile time. However, Name is a read-only variable that is set in the class's constructor and can only be changed during runtime.

The way that const and readonly are accessible is another distinction between them. Readonly values require the access level to be explicitly specified, whereas const variables are always implicitly public.

While read-only variables are determined at runtime, const values are computed at compile time.
While read-only fields might be instance-level or static and require the access level to be explicitly specified, const values are always static and automatically public.

Readonly fields can only be modified within the class constructor, but const values cannot be altered at runtime.

ReadOnly vs Constant

ReadOnly keyword Const keyword
The readonly keyword in C# can be used to create readonly fields. The const keyword in C# is used to construct constant fields.
The runtime constant ReadOnly. A compile-time constant is called const.
It is possible to modify the readonly field's value. It is not possible to modify the value of the const field.
It can't be declared within the procedure. It may be specified within the procedure.
We can set values in the constructor and declaration sections of read-only fields. We can only assign values to the declaration part of const fields.
The use of static modifiers is possible. Static modifiers are incompatible with its use.

When should you use CONSTANTS versus READONLY?

Utilize constants when the values are absolute, fixed, and do not vary, such as DaysOfWeek, MonthsOfYear, mathematical PI value, and so on. Use readonly when the variables are obtained via user input, configuration files, or any other variable that does not have the same value, such as a database connection string, a secret key, and so on.

We learned the new technique and evolved together.



About HostForLIFE.eu

HostForLIFE.eu 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 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in