C# Select clause returns system exception instead of relevant object

给你一囗甜甜゛ 提交于 2019-12-23 06:57:33

问题


I am trying to use the select clause to pick out an object which matches a specified name field from a database query as follows:

objectQuery = from obj in objectList
              where obj.Equals(objectName)
              select obj;

In the results view of my query, I get:

base {System.SystemException} = {"Boolean Equals(System.Object)"}

Where I should be expecting something like a Car, Make, or Model

Would someone please explain what I am doing wrong here?


The method in question can be seen here:

// this function searches the database's table for a single object that matches the 'Name' property with 'objectName'
public static T Read<T>(string objectName) where T : IEquatable<T>
{
    using (ISession session = NHibernateHelper.OpenSession())
    {
        IQueryable<T> objectList = session.Query<T>(); // pull (query) all the objects from the table in the database
        int count = objectList.Count(); // return the number of objects in the table
        // alternative: int count = makeList.Count<T>();

        IQueryable<T> objectQuery = null; // create a reference for our queryable list of objects
        T foundObject = default(T); // create an object reference for our found object

        if (count > 0)
        {
            // give me all objects that have a name that matches 'objectName' and store them in 'objectQuery'
            objectQuery = from obj in objectList
                          where obj.Equals(objectName)
                          select obj;

            // make sure that 'objectQuery' has only one object in it
            try
            {
                foundObject = (T)objectQuery.Single();
            }
            catch
            {
                return default(T);
            }

            // output some information to the console (output screen)
            Console.WriteLine("Read Make: " + foundObject.ToString());
        }
        // pass the reference of the found object on to whoever asked for it
        return foundObject;
    }
}

Note that I am using the interface "IQuatable<T>" in my method descriptor.

An example of the classes I am trying to pull from the database is:

public class Make: IEquatable<Make>
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Model> Models { get; set; }

    public Make()
    {
        // this public no-argument constructor is required for NHibernate
    }

    public Make(string makeName)
    {
        this.Name = makeName;
    }

    public override string ToString()
    {
        return Name;
    }

    // Implementation of IEquatable<T> interface 
    public virtual bool Equals(Make make)
    {
        if (this.Id == make.Id)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    // Implementation of IEquatable<T> interface 
    public virtual bool Equals(String name)
    {
        if (this.Name.Equals(name))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

And the interface is described simply as:

public interface IEquatable<T>
{
    bool Equals(T obj);
}

回答1:


IQueryable<T> executes your query against the backing data store (in this case, it's SQL RDBMS). Your SQL RDBMS has no idea of IEquatable<T>*, and cannot use its implementation: the query function must be translatable to SQL, and obj.Equals(objectName) is not translatable.

You can convert IQueryable<T> to IEnumerable<T> and do the query in memory, but that would be too inefficient. You should change the signature to take a Expression<Func<TSource, bool>> predicate, and pass the name checker to it:

public static T Read<T>(Expression<Func<T,bool>> pred) {
    using (ISession session = NHibernateHelper.OpenSession()) {
        return session.Query<T>().SingleOrdefault(pred);
    }
}

You can now use this method as follows:

Make snake = Read<Make>(x => x.Name == "snake");


* Additionally, your IEquatable<T> is not used in the method that you are showing: the Equals(string) method would qualify as an implementation of IEquatable<string>, but it is not mentioned in the list of interfaces implemented by your Make class.


回答2:


One of your calls to the Equal override could be throwing an exception.

For debugging purposes, try iterating rather than using Linq and you should find out which one:

foreach(object obj in objectList)
{
    bool check = obj.Equals(objectName) // set a breakpoint here
}

As far as what is causing the exception, ensure that each of your object Name and Id properties have values.



来源:https://stackoverflow.com/questions/12610731/c-sharp-select-clause-returns-system-exception-instead-of-relevant-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!