Custom LINQ Functions: Vital Tools for Every .NET Project

Tapesh Mehta Tapesh Mehta | Published on: May 15, 2024 | Est. reading time: 7 minutes
Custom LINQ Functions Vital Tools for Every .NET Project

In .NET development, Language Integrated query (LINQ) is a useful tool that allows developers to Query collections in a simple and readable manner. LINQ enforces a standard way of accessing data – whether it’s databases, XML, or in-memory collections. But LINQ is a powerful extensible tool. By writing custom LINQ functions, you can customize queries for your projects. This article explores how to write and make use of custom LINQ functions to enhance your code. We’ll cover some involved cases and show how these functions can be incorporated in different .NET projects, particularly for web development company.

Table of Contents

Understanding LINQ

Before diving into custom LINQ functions, let’s briefly revisit what LINQ is and why it’s so beneficial. LINQ is a set of methods and syntax integrated into .NET languages like C# and VB.NET, allowing developers to query and manipulate data in a more intuitive way. LINQ bridges the gap between programming languages and data storage systems, making data queries similar to writing SQL statements but with the advantage of being type-safe and integrated into the programming language.

Why Create Custom LINQ Functions?

While LINQ provides a comprehensive set of built-in methods (such as Where, Select, OrderBy, and GroupBy), there are times when the standard methods do not meet the specific requirements of your project. Custom LINQ functions allow you to:

  1. Encapsulate Complex Logic: By wrapping complex query logic in custom functions, you can simplify your code and make it more readable.
  2. Promote Reusability: Custom functions can be reused across multiple projects, promoting code consistency and reducing redundancy.
  3. Enhance Maintainability: With well-defined custom functions, your codebase becomes easier to maintain and modify.

Creating Custom LINQ Functions

It can be created using extension methods. Extension methods allow you to add new functionality to existing types without modifying their source code. Let’s look at several practical and complex examples of custom LINQ functions that can be useful in various .NET projects.

Example 1: Custom Linq Function Between Method

The Between method can be used to check if a value falls within a specified range. This is useful for filtering data based on a range of values.

public static class LinqExtensions
{
    public static bool Between<T>(this T source, T lower, T upper) where T : IComparable<T>
    {
        return source.CompareTo(lower) >= 0 && source.CompareTo(upper) <= 0;
    }
}

Usage Example:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var filteredNumbers = numbers.Where(n => n.Between(2, 4)).ToList();
// filteredNumbers will contain { 2, 3, 4 }

Example 2: DistinctBy Method

The DistinctBy method allows you to filter a collection to ensure that elements are distinct based on a specified key selector.

public static class LinqExtensions
{
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        var seenKeys = new HashSet<TKey>();
        foreach (var element in source)
        {
            if (seenKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }
}

Usage Example:

var people = new List<Person>
{
    new Person { Name = "John", Age = 30 },
    new Person { Name = "Jane", Age = 25 },
    new Person { Name = "John", Age = 40 }
};
var distinctPeople = people.DistinctBy(p => p.Name).ToList();
// distinctPeople will contain { John (Age 30), Jane (Age 25) }

Example 3: ToDictionary Method with a Custom Key Selector

The ToDictionary method can be extended to allow the creation of dictionaries with custom key selectors. This can be useful when you need to create dictionaries from collections with complex key extraction logic.

public static class LinqExtensions
{
    public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer = null)
    {
        var dictionary = new Dictionary<TKey, TSource>(comparer);
        foreach (var element in source)
        {
            dictionary[keySelector(element)] = element;
        }
        return dictionary;
    }
}

Usage Example:

var people = new List<Person>
{
    new Person { Name = "John", Age = 30 },
    new Person { Name = "Jane", Age = 25 },
    new Person { Name = "John", Age = 40 }
};
var peopleDictionary = people.ToDictionary(p => p.Name);
// peopleDictionary will contain { { "John", John (Age 40) }, { "Jane", Jane (Age 25) } }

Example 4: MaxBy Method

The MaxBy method allows you to find the element with the maximum value based on a specified key selector.

public static class LinqExtensions
{
    public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) where TKey : IComparable<TKey>
    {
        return source.Aggregate((max, current) => keySelector(current).CompareTo(keySelector(max)) > 0 ? current : max);
    }
}

Usage Example:

var people = new List<Person>
{
    new Person { Name = "John", Age = 30 },
    new Person { Name = "Jane", Age = 25 },
    new Person { Name = "Tom", Age = 35 }
};
var oldestPerson = people.MaxBy(p => p.Age);
// oldestPerson will be { Tom, Age 35 }

Example 5: MinBy Method

The MinBy method allows you to find the element with the minimum value based on a specified key selector.

public static class LinqExtensions
{
    public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) where TKey : IComparable<TKey>
    {
        return source.Aggregate((min, current) => keySelector(current).CompareTo(keySelector(min)) < 0 ? current : min);
    }
}

Usage Example:

var people = new List<Person>
{
    new Person { Name = "John", Age = 30 },
    new Person { Name = "Jane", Age = 25 },
    new Person { Name = "Tom", Age = 35 }
};
var youngestPerson = people.MinBy(p => p.Age);
// youngestPerson will be { Jane, Age 25 }

Example 6: Batch Method

The Batch method allows you to split a collection into smaller batches of a specified size. This is useful for processing large datasets in chunks.

public static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        var batch = new List<T>(size);
        foreach (var item in source)
        {
            batch.Add(item);
            if (batch.Count == size)
            {
                yield return new List<T>(batch);
                batch.Clear();
            }
        }
        if (batch.Count > 0)
        {
            yield return batch;
        }
    }
}

Usage Example:

var numbers = Enumerable.Range(1, 10).ToList();
var batches = numbers.Batch(3).ToList();
// batches will contain 4 batches: { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10} }

Example 7: GroupByMany Method

The GroupByMany method allows you to group elements by multiple keys, creating a hierarchical grouping structure.

public static class LinqExtensions
{
    public static IEnumerable<IGrouping<TKey, TSource>> GroupByMany<TSource, TKey>(this IEnumerable<TSource> source, params Func<TSource, TKey>[] keySelectors)
    {
        var result = source;
        foreach (var keySelector in keySelectors)
        {
            result = result.GroupBy(keySelector).SelectMany(group => group);
        }
        return result.GroupBy(keySelectors.Last());
    }
}

Usage Example:

var people = new List<Person>
{
    new Person { Name = "John", Age = 30, City = "New York" },
    new Person { Name = "Jane", Age = 25, City = "Los Angeles" },
    new Person { Name = "Tom", Age = 35, City = "New York" }
};
var groupedPeople = people.GroupByMany(p => p.City, p => p.Age);
// groupedPeople will create a hierarchical grouping by City and then by Age

Example 8: Page Method

The Page method allows you to implement pagination by selecting a specific page of elements from a collection.

public static class LinqExtensions
{
    public static IEnumerable<T> Page<T>(this IEnumerable<T> source, int pageNumber, int pageSize)
    {
        return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
    }
}

Usage Example:

var numbers = Enumerable.Range(1, 100).ToList();
var page = numbers.Page(2, 10).ToList();
// page will contain { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }

Integrating Custom LINQ Functions in ASP.NET Projects

Custom functions can significantly enhance the development experience in ASP.NET projects. They allow developers to write more expressive and maintainable code, which is particularly beneficial in complex web applications. For instance, an ASP.NET development company can leverage custom functions to simplify data access logic in their services and repositories.

Example: Filtering Data in ASP.NET Controllers

Let’s consider an example where an ASP.NET development company is building an API for managing products. They need to filter products based on various criteria, such as price range and category.

[HttpGet("products")]
public IActionResult GetProducts([FromQuery] decimal? minPrice, [FromQuery] decimal? maxPrice, [FromQuery] string category)
{
    var products = _productService.GetAllProducts();

    if (minPrice.HasValue && maxPrice.HasValue)
    {
        products = products.Where(p => p.Price.Between(minPrice.Value, maxPrice.Value));
    }

    if (!string.IsNullOrEmpty(category))
    {
        products = products.Where(p => p.Category == category);
    }

    return Ok(products);
}

Conclusion

.NET developers can leverage custom LINQ functions to tailor LINQ functionality to their project requirements. By separating particular logic into reusable functions, developers can make their code easier to read and maintain. Whether you’re manipulating in-memory collections, databases or any other data source, custom LINQ functions can simplify your data manipulation tasks.

Skills like these are very important for web development companies. It empowers them to develop maintainable, scalable, and robust applications rapidly. By implementing custom LINQ functions into their projects, these companies can maintain an optimized codebase that is extensible and ready for future requirements.

With all the information and examples from this post, you are able to now write and make use of custom LINQ functions in your .NET projects. Have fun coding!

clutch profile good firms
Your Software Dreams, Realized! 💭

Dream big, because at WireFuture, no vision is too ambitious. Our team is passionate about turning your software dreams into reality, with custom solutions that exceed expectations.

Hire Now

Categories
.NET Development Angular Development JavaScript Development KnockoutJS Development NodeJS Development PHP Development Python Development React Development Software Development SQL Server Development VueJS Development All
About Author
wirefuture - founder

Tapesh Mehta

verified Verified
Expert in Software Development

Tapesh Mehta is a seasoned tech worker who has been making apps for the web, mobile devices, and desktop for over 13+ years. Tapesh knows a lot of different computer languages and frameworks. For robust web solutions, he is an expert in Asp.Net, PHP, and Python. He is also very good at making hybrid mobile apps, which use Ionic, Xamarin, and Flutter to make cross-platform user experiences that work well together. In addition, Tapesh has a lot of experience making complex desktop apps with WPF, which shows how flexible and creative he is when it comes to making software. His work is marked by a constant desire to learn and change.

Get in Touch
Your Ideas, Our Strategy – Let's Connect.

No commitment required. Whether you’re a charity, business, start-up or you just have an idea – we’re happy to talk through your project.

Embrace a worry-free experience as we proactively update, secure, and optimize your software, enabling you to focus on what matters most – driving innovation and achieving your business goals.

Hire Your A-Team Here to Unlock Potential & Drive Results
You can send an email to contact@wirefuture.com
clutch profile good firms award-3 award-4 award-5 award-6