locked
"Foreach" block doesn't seem to get skipped when the input data is null, causing exception RRS feed

  • Question

  • User-1431905850 posted

    I'll try to describe this without overloading everyone with too much background information, but the short and sweet version is that I am working on a bibliographic application which, among other things, analyzes the text of documents to generated inverted lists that are searchable by the individual words ("descriptors") and provide the document IDs in which they occur, and the number of occurrences in each document.   Naturally there's a database involved, but at this point, for this particular dataset, the table in SQL Server is empty and everything is happening with like-defined records in memory.

    At this point, I'm working on a document by document basis, but will eventually want to process this data in larger sets once I have confirmed that everything works at the unit level.

    My data is defined as follows:

    Collection: A set of documents identified for collective processing and analysis.  Identified by a 7-digit integer.

    CollectionDetail: The individual documents identified by a Collection, having typical bibliographic identifying data such as author, title, call number; and also a brief abstract on which the subject analysis is based.  In this case, the abstracts are mostly just the subject cataloging descriptor entries.  A CollectionDetail object is identified by the CollectionID with which it is associated plus a SequenceNumber field, also an integer.

    InvertedList: As described above.  The fields are Descriptor, CollectionID, SequenceNumber, and Occurrences.

    The CollectionDetail partial class includes a method to generate the InvertedList records in the database for that document.  Before I describe how that method is supposed to work, I should point out that I also have a static class ExtendIQueryable which simply provides a convient way of comparing two IQueryables to perform minus and subsetting queries.

        public static class ExtendIQueryable
        {
            public static IQueryable<T> In<T>(this IQueryable<T> source, IQueryable<T> checkAgainst)
            {
                return from s in source
                       where checkAgainst.Contains(s)
                       select s;
            }
    
            public static IQueryable<T> NotIn<T>(this IQueryable<T> source,
                                              IQueryable<T> checkAgainst)
            {
                return from s in source
                       where !checkAgainst.Contains(s)
                       select s;
            }
        }


    The method in the CollectionDetail partial class starts as follows, to "calculate" the InvertedList records based on what the documents contain, and also to determine what IL records already exist in the database.   These results are ProspectiveILRecs and ExistingILRecs respectively.

            public bool DocumentUpdateIVRecsDB()
            {
                InvertedListRepository ivr = new InvertedListRepository();
                MasterStopListRepository mslr = new MasterStopListRepository();
                
                IQueryable<InvertedList> ProspectiveILRecs=this.GenDocumentInvertedFileRecords();
                
                IQueryable<InvertedList> ExistingILRecs =
                    from Details in idc.InvertedLists
                    where Details.CollectionID == this.CollectionID
                    && Details.SequenceNumber == this.SequenceNumber
                    select Details;
    

    Because I don't yet have anything in the InvertedList table in the database, ExistingILRecs comes back empty as I expect.  ProspectiveILRecs may have a few records or a great many, but by definition will always have some.

    The next step is to compare the two IQueryables, because anything currently in the database not appearing in the prospective list would have to be deleted, if they were present.

                //First delete the existing IL records that we no longer need.
                IQueryable<InvertedList> ILRecsToDelete = ExtendIQueryable.NotIn(ExistingILRecs, ProspectiveILRecs);
    


    followed by the "foreach" block

                foreach (InvertedList ilrec in ILRecsToDelete)
                {
                    InvertedList il = ivr.GetSingleIVLRecord(ilrec.CollectionID, ilrec.SequenceNumber, ilrec.DescriptorValue);
                    ivr.Delete(il);
                }

    And this seems to be where the program fails.  The error message I get is: "An IQueryable that returns a self-referencing Constant expression is not supported."   As far as I can determine from the debugger, it's coming from the "foreach" block, which somehow is failing to recognize that there is nothing to process.

    Saturday, July 10, 2010 5:58 PM

All replies

  • User-366246501 posted

    Try doing the selection of the first entity in the query itself using FirstOrDefault and then check if the result is null or not

    In the standard implementation of linq, the operators "select" and "where" map to methods that return an IEnumerable or IQueryable. So standard linq methods when used should always return an IEnumerable from your query not a single object.

    But linq methods that are candidates for the linq operators are not restricted to methods returning IEnumerables, any method returning anything can be chosen.

    In case you have instance methods named "Select" and "Where" that return a single object or extensions methods that are specific to your class and return a single object those will be used instead of the standard linq ones.

    My guess is that either a "Select" or "Where" method defined in your class is making linq return a single value instead of a IQueryable<T>.

    Saturday, July 10, 2010 8:09 PM
  • User-1431905850 posted


    Try doing the selection of the first entity in the query itself using FirstOrDefault and then check if the result is null or not 


    Thanks Ajay, your response is appreciated.


    Do you mean, like this?



               InvertedList FirstOrDefaultIL = ILRecsToDelete.FirstOrDefault<InvertedList>();
    






    This call generates the exception.  

    ETA: To clarify, I mean that I get the same exception with this call now, that I did initially from somewhere inside the foreach.


    Saturday, July 10, 2010 11:26 PM
  • User-366246501 posted

    Do one thing send me your complete file, i try my best.

    Sunday, July 11, 2010 12:01 AM
  • User-1431905850 posted

    Sent you an email. 

    As I mentioned there, I found that the .FirstOrDefault query works if the IQueryable isn't null.   You'd think the software would be more robust, but there you go.   It probably does help to encourage more efficient dev work at that.

    I'm thinking I probably need to evaluate the result of the database query differently.  It's probably better to check the query result and not try to form the IQueryable at all if it's null.


    Sunday, July 11, 2010 5:46 PM
  • User-366246501 posted

    hi,

    is your problem solved?

    Tuesday, July 27, 2010 7:13 PM
  • User-366246501 posted

    If your question has been answered in such a way that your problem is solved, then please mark the question as solved (preferably marking the post that answered your question). 

    If you have any solution please post it so that it help others.

    This will allow others who experience similar trouble to find that this question has been answered and also allows the rest of us to know that your problem is solved.

    Thursday, August 5, 2010 11:53 AM