简体   繁体   中英

How to create a dynamic filter using the attributes of a class managed by the Entity Framework?

I'm developing an application using Entity Framework 6, and I'm not able to resolve the issue of dynamic filters using the properties of an entity class managed by Entity Framework.

The problem in fact is the non-nullable properties (which can be compared to null) of the class.

Background

I'm using a conventional framework separating the application into 3 projects (Model, View, Controller) with C#.

Using SQL Server 2017 as a database and Entity Framework (not the Core).

What I have tried

I did several Google queries but none of them approached my problem.

The answer that most approached the solution was this here Entity Framework Core - dynamic filtering user Marcio Martins, but I did not see anything about how to handle non-nullable properties .

Would anyone have an idea how to fix this?

Code

I need to see a way to translate the query below into a lambda or linq expression:

-- Sql Server query
declare @ProductID int
declare @Name nvarchar(600)
declare @BarCode nvarchar(26)
declare @Active bit
declare @Price decimal

select
    p.ProductID
    , p.Name
    , p.BarCode
    , p.Active
    , p.Price
from
    Product p
where
    (@ProductID is null or p.ProductID = @ProductID)
    and (@Name is null or p.Name = @Name)
    and (@BarCode is null or p.BarCode = @BarCode)
    and (@Active is null or p.Active = @Active)
    and (@Price is null or p.Price = @Price)

The entity class

public class Package
{
    [Key]
    public Int32 PackageID { get; set; }
    [Required]
    [MaxLength(20)]
    public String Name { get; set; }
    public Boolean Active { get; set; }
}

The snippet code from View.

List<Package> packages = new PackageController().Retrieve();
// Dynamic query here!

EDIT

I tried this, but it was not functional:

List<Package> packages = new PackageController().Retrieve()
                                                .Where(p =>  String.IsNullOrEmpty(p.Name) || p.Name.Contains(package.Name))
                                                .ToList();

Expected:

The main idea is to make the query flexible so that the end user can filter the data by any of the properties of the entity class.

Ex: the user selects the name or part of the package name and DOES NOT indicate whether the records are active or not, considering the above query, only the Name attribute would be informed, the Active attribute would receive the null value, and the query would return all the values where Package Name is the one selected by the user regardless if it is active or deactivated.

Actual results

As I can not adjust the dynamic filter, then if the user informs the "Name" property of the package or part of the "Name" and does not say whether it is active or not, .NET automatically initializes the value of the "Active" property of the Package to false, and this ends up returning all disabled Packages with the name or part of the name selected by the user.

Make property Active nullable (so it matches the field type in the database) and it will solve your problem.

Entity class:

public class Package
{
    ...
    public Boolean? Active { get; set; }
}

Thank you so much for seeing and responding.

I was able to SOLVE THE PROBLEM after some tests and an idea that Przemek Marcinkiewicz gave me in his answer.

Come on, this answer can help many in the future.

I needed to make the properties of the Package class be nullable, but without changing the bank model, I did this by configuring the class like this:

public class Package
{
    [Key]
    [Required]
    public Int32? PackageID { get; set; }
    [Required]
    [MaxLength(20)]
    public String Name { get; set; }
    [Required]
    public Boolean Active? { get; set; }
}

So far, okay, with this I needed to somehow use the mechanism of my query, and I was able to do it by calling the Entity framework called the procedure and with nullable parameters, the final result was like this:

public List<Package> Retrieve(Package entity)
{
    using (MyDatabaseContext db = new MyDatabaseContext())
    {
        return db.Database
            .SqlQuery<Package>
            (
                "PROC_PACKAGE_SELECT @PackageID, @Name, @Active"
                , new SqlParameter("@PackageID", (object)entity.PackageID ?? DBNull.Value)
                , new SqlParameter("@Name", (object)entity.Name ?? DBNull.Value)
                , new SqlParameter("@Active", (object)entity.Active ?? DBNull.Value)
            )
                .ToList<Package>();
    }
}

I ran several tests, and in that case the query was 100% flexible for my needs.

I really hope this effort can help someone in the community.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM