【问题标题】:C# Generic search in object recursively [duplicate]C#递归地在对象中进行通用搜索[重复]
【发布时间】:2020-03-19 10:52:25
【问题描述】:

我正在尝试编写一个用于所有对象的通用搜索。 我有这段代码,它可以很好地搜索一个对象的属性,但我也想搜索相关对象的属性。

例如。我有这些模型/对象

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address{ get; set; }

    public ICollection<Contract> Contracts { get; set; }
}
public class Contract
{
    public int Id { get; set; }
    public DateTime From{ get; set; }
    public DateTime To{ get; set; }
    public string Comment{ get; set; }
    public int CustomerId { get; set; }

    [ForeignKey("CustomerId")]
    public Customer Customer { get; set; }
}

我想搜索是否有任何属性包含一些字符串,例如。 “彼得”,我会这样称呼它:

string searchString = "Peter";
var customers = db.Customers
    .Include(x => x.Contracts)
    .WhereAnyPropertiesOfSimilarTypeContains(searchString);

此代码将检查“客户”的任何属性是否包含字符串“彼得”。 但我还需要检查相关模型“合同”是否包含“彼得。

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

            var predicate = PredicateBuilder.False<TEntity>(); //--- True to equal
            var entityFields = GetEntityFieldsToCompareTo<TEntity, TProperty>();
            foreach (var fieldName in entityFields)
            {
                MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });

                var predicateToAdd = Expression.Lambda<Func<TEntity, bool>>(
                    Expression.Call(
                        Expression.PropertyOrField(param, fieldName), method,
                        Expression.Constant(value)), param);

                predicate = predicate.Or(predicateToAdd);  //--- And to equal
            }

            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.
        public 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);
        }
    }

谢谢。

【问题讨论】:

  • 反射...是我在这里看到的唯一方式。
  • 你能说得更具体点吗?
  • 无限递归 - 是阻止我发布答案的原因。
  • 分享你得到的。也许我会想出点什么。
  • 有很多问题展示了如何递归地获取包括子对象在内的所有属性 - 我选择了一个作为重复项。如果那不是您要找的东西 - edit 需要澄清的问题。请注意,大多数答案都假设对象树而不是带有循环的图 - 确保也澄清这一点(同样简单地不访问同一对象两次通常就足够且易于检查)。此外,您还用“泛型”而不是“反射”标记了问题 - 澄清一下为什么您正在寻找基于泛型的解决方案也会很有用。

标签: c# generics


【解决方案1】:

重读问题后。我不知道你在尝试什么,但我在这里提出了我有你在寻找什么的想法。

public class Customer : AbstractEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }

    public ICollection<Contract> Contracts { get; set; }
}
public class Contract : AbstractEntity
{

    //what property here can be string "Peter"? Comments?
    //what are you trying?

    public int Id { get; set; }
    public DateTime From { get; set; }
    public DateTime To { get; set; }
    public string Comment { get; set; }
    public int CustomerId { get; set; }

    [ForeignKey("CustomerId")]
    public Customer Customer { get; set; }
}

public abstract class AbstractEntity
{

    //this method can be used to preselect properties you want
    protected virtual Tuple<bool, ICollection<PropertyInfo>> PropertyCollector()
    {
        return new Tuple<bool, ICollection<PropertyInfo>>(false, null);
    }

    public IEnumerable<Tuple<Type, object>> GetRowValues()
    {
        foreach (var prop in GetRows())
        {
            yield return new Tuple<Type, object>(prop.PropertyType, prop.GetValue(this));
        }
    }

    public ICollection<PropertyInfo> GetRows()
    {

        var tuple = PropertyCollector();

        ISet<PropertyInfo> pInfo;

        if (tuple.Item1)
        {
            pInfo = new HashSet<PropertyInfo>(tuple.Item2);
        }
        else //search all non virtual, private, protected properties, "following POCO scheme"
        {
            pInfo = new HashSet<PropertyInfo>();

            foreach (var prop in GetType().GetProperties())
            {
                foreach (var access in prop.GetAccessors())
                {
                    if ((!access.IsVirtual && !access.IsPrivate) && (prop.CanWrite && prop.CanRead))
                    {
                        pInfo.Add(prop);
                    }
                }
            }
        }
        return pInfo;
    }
}


public static class Searchs
{

    public static ICollection<object> ObjectsWithStringFound(ICollection<Customer> customers, string toBeFound)
    {

        var objs = new List<object>();

        foreach (var cust in customers)
        {
            var strings = cust.GetRowValues().Where(tpl => tpl.Item1 == typeof(string)).Select(tpl => tpl.Item2);
            var contracts = cust.GetRowValues().Where(tpl => tpl.Item2 is IEnumerable<Contract>).Select(tpl => tpl.Item2);

            if (strings.Any(str => str == toBeFound))
            {
                objs.Add(cust);
            }
            else if (contracts.Any(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound))
            { //What I suppose I must "match" with "Peter"??!?!
                objs.Add(contracts.First(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound));
            }

        }

        return objs;

    }
}

我认为我们彼此不理解。

【讨论】:

  • 什么是'p'?在这一行中: if ((!access.IsVirtual && !access.IsPrivate) && !p.Contains(prop) && (prop.CanWrite && prop.CanRead))
  • @MartinNohejl Ups 错字。 pInfo,避免重复属性。您可以使用 ISet 类。我年轻而愚蠢。
  • @MartinNohejl 所有这一切的重要部分是我回答的最后一部分。在集合内的集合中搜索
  • 我可以看到。但我不知道为什么,但我收到一个错误:错误 CS1061 'IIncludableQueryable>' 不包含 'GetRowValues' 的定义并且没有可访问的扩展......
  • 嗯,智能感知怎么说?
猜你喜欢
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-02
  • 2014-11-27
  • 1970-01-01
相关资源
最近更新 更多