2

in LINQ how do i search all fields in a table, what do i put for ANYFIELD in the below?

Thanks

var tblequipments = from d in db.tblEquipments.Include(t => t.User).Include(t => t.ChangeLog).Include(t => t.AssetType)
                                where d."ANYFIELD" == "VALUE" select d;
Ocelot20
  • 10,510
  • 11
  • 55
  • 96
AlexW
  • 2,843
  • 12
  • 74
  • 156

4 Answers4

5

You can't. You must compare each field individually. It doesn't make sense to compare all fields, given a field may not even be of the same type as the object you're comparing to.

Kenogu Labz
  • 1,094
  • 1
  • 9
  • 20
  • Not through LINQ, for sure, and that's as it should be. – Kenogu Labz Jul 31 '13 at 17:38
  • You can't make a statement about how anything should be because you can't know every application that could benefit from being able to do this. What if the table is all one type and you want to search every field? I could conceive of several scenarios where this would be beneficial. Actually, that's the reason I found this post; I want to do exactly what was originally requested. It's much more user-friendly for my users to be able to type whatever they want into a box and pull up a record without clicking any additional buttons. Simple: type any relevant piece of info and hit search. Google? – Anthony Aug 23 '14 at 22:27
  • That's not what LINQ is meant for, though. And that's the trick: wanting to do something is fine, but it isn't fine to do it through a tool that it isn't intended to be used in that way. In a case like this, perhaps a change of model is in order. The properties should likely be treated as objects of their own using the Component pattern or simple aggregation / delegation. – Kenogu Labz Sep 03 '14 at 00:35
3

You can, using reflection. Try this:

    static bool CheckAllFields<TInput, TValue>(TInput input, TValue value, bool alsoCheckProperties)
    {
        Type t = typeof(TInput);
        foreach (FieldInfo info in t.GetFields().Where(x => x.FieldType == typeof(TValue)))
        {
            if (!info.GetValue(input).Equals(value))
            {
                return false;
            }
        }
        if (alsoCheckProperties)
        {
            foreach (PropertyInfo info in t.GetProperties().Where(x => x.PropertyType == typeof(TValue)))
            {
                if (!info.GetValue(input, null).Equals(value))
                {
                    return false;
                }
            }
        }
        return true;
    }

And your LINQ query:

var tblequipments = from d in db.tblEquipments.Include(t => t.User).Include(t => t.ChangeLog).Include(t => t.AssetType)
where CheckAllFields(d, "VALUE", true) select d;

The third parameter should be true if you want to check all fields and all properties, and false if you want to check only all fields.

ProgramFOX
  • 6,131
  • 11
  • 45
  • 51
  • Was just about to post a similar response, but on IQueryable. This one will work with LINQ-to-Objects, which this is tagged as, but I presume it won't work in his actual scenario since he uses `db.tblEquipments` which implies that he's using EF or Linq-2-SQL. – Ocelot20 Jul 30 '13 at 17:06
  • @Ocelot20: Perhaps my solution won't work, but the OP can always try it. – ProgramFOX Jul 30 '13 at 17:09
  • I am using Entity Framework, have i tagged this incorrectly? sorry for that! will this function work? ive tried using it, but on the alsocheckproperties statement, i get "no overload for method 'GetValue' takes 1 arguments Thanks – AlexW Jul 31 '13 at 08:19
  • @AlexW: Have you added `using System.Reflection;` at the top of your code file? – ProgramFOX Jul 31 '13 at 08:23
  • yes, all is ok apartfrom the error on the foreach propertyinfo – AlexW Jul 31 '13 at 08:33
  • ok, error has gone, testing the function i get "LINQ to Entities does not recognize the method 'Boolean CheckAllFields[tblEquipment,String](ITAPP.Models.tblEquipment, System.String, Boolean)' method, and this method cannot be translated into a store expression." – AlexW Jul 31 '13 at 08:47
  • @AlexW: It doesn't work because LINQ to Entities tries to convert the CheckAllFields call to SQL, which isn't possible. So unfortunately, my solution won't work for LINQ to Entities. – ProgramFOX Jul 31 '13 at 08:59
  • 2
    @AlexW: Unfortunately, no. As you can read [in this answer](http://stackoverflow.com/a/17187582/2619912), it's not possible to use custom methods because LINQ to Entities needs to be able to translate the expression to SQL. – ProgramFOX Jul 31 '13 at 09:03
1

EDIT: Someone already built this...see here.

Not a full answer, but I don't agree with assertion that you simply can't...

You could come up with an extension method that dynamically filtered the IQueryable/IEnumerable (I'm guessing IQueryable by the db variable) based on properties of a similar type for you. Here's something whipped up in Linqpad. It references PredicateBuilder and is by no means complete/fully accurate, but I tested it out in Linq-to-SQL on some of my tables and it worked as described.

void Main()
{
    YourDbSet.WhereAllPropertiesOfSimilarTypeAreEqual("A String")
         .Count()
         .Dump();
}

public static class EntityHelperMethods
{
    public static IQueryable<TEntity> WhereAllPropertiesOfSimilarTypeAreEqual<TEntity, TProperty>(this IQueryable<TEntity> query, TProperty value)
    {
        var param = Expression.Parameter(typeof(TEntity));

        var predicate = PredicateBuilder.True<TEntity>();

        foreach (var fieldName in GetEntityFieldsToCompareTo<TEntity, TProperty>())
        {
            var predicateToAdd = Expression.Lambda<Func<TEntity, bool>>(
                Expression.Equal(
                    Expression.PropertyOrField(param, fieldName),
                    Expression.Constant(value)), param);

            predicate = predicate.And(predicateToAdd);
        }

        return query.Where(predicate);
    }

    // TODO: You'll need to find out what fields are actually ones you would want to compare on.
    //       This might involve stripping out properties marked with [NotMapped] attributes, for
    //       for example.
    private static IEnumerable<string> GetEntityFieldsToCompareTo<TEntity, TProperty>()
    {
        Type entityType = typeof(TEntity);
        Type propertyType = typeof(TProperty);

        var fields = entityType.GetFields()
                            .Where (f => f.FieldType == propertyType)
                            .Select (f => f.Name);

        var properties = entityType.GetProperties()
                                .Where (p => p.PropertyType == propertyType)
                                .Select (p => p.Name);

        return fields.Concat(properties);
    }
}

Useful resources for the unresolved part:

Community
  • 1
  • 1
Ocelot20
  • 10,510
  • 11
  • 55
  • 96
  • Sorry, just re-read and noticed it was "Any field", not "All fields". Fortunately that would just be a metter of using `predicate.Or` instead of `predicate.And`. – Ocelot20 Jul 31 '13 at 17:35
1

if this help some one.

first find all properties within Customer class with same type as query:

var stringProperties = typeof(Customer).GetProperties().Where(prop =>
    prop.PropertyType == query.GetType());

then find all customers from context that has at least one property with value equal to query:

context.Customer.Where(customer => 
    stringProperties.Any(prop =>
        prop.GetValue(customer, null) == query));
Monojit Sarkar
  • 2,353
  • 8
  • 43
  • 94