1

I want to select available spotId's in my database. I have this method:

public ActionResult ShowAvailableSpots(int Id, DateTime ArrivalDate, DateTime LeaveDate)
{
    var query2 = (from r in db.Reservations
                where (DbFunctions.TruncateTime(r.ArrivalDate) >= DbFunctions.TruncateTime(ArrivalDate)
                    && DbFunctions.TruncateTime(r.LeaveDate) <= DbFunctions.TruncateTime(LeaveDate))
                  select r.spot);

    ViewBag.StartingDate = ArrivalDate;
    ViewBag.EndingDate = LeaveDate;
    ViewBag.AvailableSpots = query2;

    ViewBag.CampingSpotId = new SelectList(query2, "CampingSpotId", "SpotName");

    return View();
}

I made sure there's no reservation within the given date range, then why are there no spot id's returned?

The output generated by the query is as follows:

SELECT
   [Extent2].[campingspotid] AS [CampingSpotId],
   [Extent2].[spotname]      AS [SpotName],
   [Extent2].[fieldname]     AS [FieldName],
   [Extent2].[surface]       AS [Surface],
   [Extent2].[wifi]          AS [Wifi],
   [Extent2].[water]         AS [Water],
   [Extent2].[sewer]         AS [Sewer],
   [Extent2].[reserved]      AS [Reserved],
   [Extent2].[booked]        AS [Booked],
   [Extent2].[spotprice]     AS [SpotPrice],
   [Extent2].[type]          AS [Type]   
FROM
   [dbo].[reservations] AS [Extent1]          
INNER JOIN
   [dbo].[campingspots] AS [Extent2]                  
      ON [Extent1].[campingspotid] = [Extent2].[campingspotid]   
WHERE
   (
      (
         CONVERT (DATETIME2, CONVERT(VARCHAR(255), [Extent1].[arrivaldate],                                  102), 102                     )            
      ) >= (
         CONVERT (DATETIME2, CONVERT(VARCHAR(255), @p__linq__0, 102), 102                   ) 
      ) 
   )          
   AND (
      (
         CONVERT (DATETIME2, CONVERT(VARCHAR(255), [Extent1].[leavedate],                                      102), 102)                
      )                    <= (
         CONVERT (DATETIME2, CONVERT(VARCHAR(255), @p__linq__1, 102                                             ), 102)                       
      ) 
   )

PS: I use TruncateTime because of this

EDIT: Here's my Reservation model:

public class Reservation
{
    [Key]
    public int ReservationId { get; set; }

    [DataType(DataType.Date)]
    public DateTime ArrivalDate { get; set; }

    [DataType(DataType.Date)]
    public DateTime LeaveDate { get; set; }
    //Vreemdesleutel van Plek

    public int CampingSpotId { get; set; }

    public virtual CampingSpot spot { get; set; }

}

Here's my campingspot model:

public class CampingSpot
{
    [Key]
    [Required(ErrorMessage = "Please select at least one CampingSpotID")]
    public int CampingSpotId { get; set; }
    public string SpotName { get; set; }  
}

New queryoutput looks like: SELECT CAST(NULL AS int) AS [C1], CAST(NULL AS datetime2) AS [C2], CAST(NULL AS datetime2) AS [C3], CAST(NULL AS int) AS [C4], CAST(NULL AS int) AS [C5], CAST(NULL AS int) AS [C6] FROM ( SELECT 1 AS X ) AS [SingleRowTable1] WHERE 1 = 0

The output above was generated by this query:

var res = db.Reservations.Where(c => DbFunctions.TruncateTime(c.ArrivalDate) >= DbFunctions.TruncateTime(ArrivalDate)
                                       && DbFunctions.TruncateTime(c.LeaveDate) <= DbFunctions.TruncateTime(LeaveDate)
                                       && c.CampingSpotId == null); 
Community
  • 1
  • 1
Forza
  • 1,619
  • 3
  • 28
  • 49
  • Do not pass "live" `IQueryable` objects to your `ViewBag`/`ViewModel`/`ViewData`, instead execute the query within your Controller's Action method and pass the instantiated results to the view. – Dai Mar 03 '15 at 22:56
  • As for the query itself, run it from within MySQL Workbench and see what results it gives. That said, the SQL generated doesn't look like MySQL's dialect to me. What DBMS are you using? – Dai Mar 03 '15 at 22:58
  • it is LINQ converted to SQL. I use visual studio 2013 to generate and manage the database. – Forza Mar 03 '15 at 23:00
  • If VS is generating the database for you then you'd be using Microsoft SQL Server, not MySQL Server. Why is this question tagged with MySQL Server? – Dai Mar 03 '15 at 23:01
  • Oops :) Changed the tag for you – Forza Mar 03 '15 at 23:02
  • What happens when you run the query in SQL Server Management Studio or a VS `.sql` editor against your database? – Dai Mar 03 '15 at 23:03
  • SQL Formatter / Beutifier can go a long way ;-) http://www.freeformatter.com/sql-formatter.html#ad-output – Michal Ciechan Mar 03 '15 at 23:03
  • Was about to edit it. Thx Michal :) – Forza Mar 03 '15 at 23:06
  • @Dai when I run the query in VS i get this: Msg 137, Level 15, State 2, Line 23 Must declare the scalar variable "@p__linq__0". – Forza Mar 03 '15 at 23:09
  • You are getting an error because you need to declare the SQL variable "@p__linq__0", you will also have to declare "@p__linq__1", and set them to a value – Michal Ciechan Mar 03 '15 at 23:21
  • These are declared. it is the ArrivalDate and LeaveDate which are also passed to the viewbag for reference. When LINQ is compiled to SQL these variables are always looking like that for some reason.. – Forza Mar 03 '15 at 23:28
  • Yes, because EF passes them in as SQL parameters, this is for safety to prevent SQL Injection as well as allow the server to cache queries as the body will not change, only the parameter. – Michal Ciechan Mar 03 '15 at 23:30

1 Answers1

7

I can see from your generated SQL, it is doing a FROM Reservations with INNER JOIN on CampingSpots, and you've mentioned you made sure there ano reservations for this time date, therefore no results...

EDIT 1 AS per your comment, if you want all camping spots which you have no reservations, you want to do

FROM CampingSpots cs 
LEFT JOIN Reservations r 
WHERE r.ID IS NULL

And in your Linq2Entitis, something like:

    public class Reservation
    {
        public DateTime ArrivalDate { get; set; }
        public DateTime LeaveDate { get; set; }
    }

    public class CampingSpot
    {
        public virtual Reservation Reservation { get; set; }

    }
    public class TestClass
    {
        public void Test()
        {            
            var CampingSpots = new List<CampingSpot>().AsQueryable();

            var ArrivalDate = new DateTime();
            var LeaveDate = new DateTime();

            var res = CampingSpots.Where(c => DbFunctions.TruncateTime(c.ArrivalDate) >= DbFunctions.TruncateTime(ArrivalDate)
                                           && DbFunctions.TruncateTime(c.LeaveDate) <= DbFunctions.TruncateTime(LeaveDate)
                                           && c.Reservation == null)
                                 ).ToList();                
        }
    }

EDIT 2

Ok you will need to add Reservation into CampingSpot model as a navigation property.

public class CampingSpot
{
    [Key]
    [Required(ErrorMessage = "Please select at least one CampingSpotID")]
    public int CampingSpotId { get; set; }
    public string SpotName { get; set; }  

    public virtual int ReservationId { get; set; }
}

And then modify your Controller Action to:

        public ActionResult ShowAvailableSpots(int Id, DateTime ArrivalDate, DateTime LeaveDate)
        {
            var query2 = db.CampingSpots.Where(c => DbFunctions.TruncateTime(c.ArrivalDate) >= DbFunctions.TruncateTime(ArrivalDate)
                                           && DbFunctions.TruncateTime(c.LeaveDate) <= DbFunctions.TruncateTime(LeaveDate)
                                           && c.Reservation == null)
                                 ).ToList();

            ViewBag.StartingDate = ArrivalDate;
            ViewBag.EndingDate = LeaveDate;
            ViewBag.AvailableSpots = query2;

            ViewBag.CampingSpotId = new SelectList(query2, "CampingSpotId", "SpotName");

            return View();
        }

EDIT 3

I just noticed what you are doing. if camping Spots has no ArrivalDate and LeaveDate. You will need to get the reservations between that time, and then in C# figure out what dates are actually free. So yes your original query is correct, but you will need to manually go over each "Day/Period" and calculate if it is free. If you receive no results, it means that camping spot is free for the whole of that period, as it has no reservations. IF it does have reservations, you either need to come back saying taken, or do calculations to figure out which days excactly are taken, and which are free.

EDIT 4

            var query2 = db.CampingSpots
                .Where(c => !db.Reservations.Any(r => 
                               DbFunctions.TruncateTime(r.ArrivalDate) >= DbFunctions.TruncateTime(ArrivalDate)
                            && DbFunctions.TruncateTime(r.LeaveDate) <= DbFunctions.TruncateTime(LeaveDate)                 
                )).ToList();

EDIT 5

You probably want something like this, in case

1234567 < --- Day
|---|   < --- Reservation
  |---| < --- Query

Which would not match Edit 4, as arrival date AND leave date is not between. So you need to check with below:

            var query2 = db.CampingSpots
                .Where(c => !db.Reservations.Any(r => 
                               (DbFunctions.TruncateTime(r.ArrivalDate) >= DbFunctions.TruncateTime(ArrivalDate)
                            && DbFunctions.TruncateTime(r.ArrivalDate) < DbFunctions.TruncateTime(LeaveDate))    
                              ||
                               (DbFunctions.TruncateTime(r.LeaveDate) <= DbFunctions.TruncateTime(LeaveDate)
                            && DbFunctions.TruncateTime(r.LeaveDate) > DbFunctions.TruncateTime(ArrivalDate))             
                )).ToList();
Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • That is exactly what I want to have. I want to have all CampingSpots which DO NOT have a reservation on the given date range.. – Forza Mar 03 '15 at 23:07
  • thx for your edit. How do I view the results only for the given daterange? Now it is for the campingspots which have no reservation at all. – Forza Mar 03 '15 at 23:13
  • See latest edit, in future please post code like i did, with minimal models as well so we can test and have intelisense – Michal Ciechan Mar 03 '15 at 23:18
  • On another note, is you ArrivalDate and LeaveDate a DateTime object? – Michal Ciechan Mar 03 '15 at 23:22
  • Ignore last comment, i can see from meth sig it is – Michal Ciechan Mar 03 '15 at 23:23
  • Im really curious as to why you need the TruncateTime :| – Michal Ciechan Mar 03 '15 at 23:23
  • see the bottom of the OP for truncate time :) – Forza Mar 03 '15 at 23:26
  • It does not work yet, but I feel we are getting close. Should I post the model in OP? – Forza Mar 03 '15 at 23:26
  • Do you have reservation as a nvaigation property? could you post the enw SQL and models as EDIT – Michal Ciechan Mar 03 '15 at 23:27
  • Could you post generated SQL – Michal Ciechan Mar 03 '15 at 23:34
  • Yes, check bottom of the OP – Forza Mar 03 '15 at 23:36
  • I have slightly modified the code you posted here to accomodate to my models. The ArrivalDate and LeaveDate for example are stored in Reservation model – Forza Mar 03 '15 at 23:38
  • Ahh yes that's correct. So now the original query is correct, I need to figure out how to return the spotID if there's no reservation found. As CampingSpot is FK in reservation, there's no campingspot to be found if there are no reservations :) – Forza Mar 03 '15 at 23:46
  • Oh you want All spot ids for which there are no reservations? rather than reservations for that one sepcific spot? – Michal Ciechan Mar 04 '15 at 00:05
  • Yes, correct! I want all spot ID's so the user can select one :) – Forza Mar 04 '15 at 00:10
  • Notice that in your code and in your explanation you still refer to CampingSpot model as having the ArrivalDate and LeaveDate, but in fact they are in the Reservation model. See the edit in the OP :) – Forza Mar 04 '15 at 00:11
  • See last edit. you want Where(!db.Reservations.Any(...)) – Michal Ciechan Mar 04 '15 at 00:11
  • Unfortunately, it is not working properly yet :( Do you still have time/commitment to help me with this? – Forza Mar 06 '15 at 17:00
  • I have new datechecks, these are OK now. However it is returning ALL campingspots or NONE campingspots instead of just the spots that are available. Any ideas for that? – Forza Mar 06 '15 at 18:11
  • it shouldnt be returning none or all, what you probably see is happening is, if arrival >= arrival date, BUT leave date isnt, it will return it. – Michal Ciechan Mar 06 '15 at 23:08
  • See edit, if still wrong, post generated SQL (formatteD) – Michal Ciechan Mar 06 '15 at 23:14
  • @Michael, thank you for the edit. You rock :) I have the question answered here: http://stackoverflow.com/questions/28905393/c-return-any-item-that-matches-the-condition?noredirect=1#comment46070105_28905393 – Forza Mar 08 '15 at 17:54