A few Dlinq bugs

I run into a few issues while testing Dlinq out. Here is a simple program that demonstrates them. Not sure if all of them are real bugs as some could be caused by my mistakes.

using System;

using System.Collections.Generic;

using System.Text;

using System.Query;

using System.Xml.XLinq;

using System.Data.DLinq;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LINQConsoleApplication

{

class Program

{

static void Main (string[] args)

{

DlinqDemo db = new DlinqDemo(@"Data Source=localhost\sqlexpress;Initial Catalog=DlinqDemo;Integrated Security=True");

//create some seed data for the demo

Hospital hos = new Hospital(){Name = "FastServiceHospital"};

Doctor doc = new Doctor(){Name = "Mr. Smith", Hospital = hos};

Patient pat = new Patient () { Name = "Mr. Sick", Doctor = doc, Age = 50, Enrolldate = DateTime.UtcNow };

db.Patients.Add(pat);

db.SubmitChanges();

/***********************************************/

/* issue with RejectChanges() */

// the case that works...

Hospital hos1 = new Hospital(){Name = "FastServiceHospital"};

db.Hospitals.Add(hos1);

try{

db.SubmitChanges();

}

catch(Exception e)

{

//Exception due to primary key constraint

db.RejectChanges();

}

//succeed

db.SubmitChanges();

//the case that fails...

Patient pat1 = new Patient(){Name = "Mr. Sick", Doctor = doc, Age = 50, Enrolldate = DateTime.UtcNow};

try{

db.SubmitChanges();

}

catch(Exception e)

{

//Exception due to primary key constraint

db.RejectChanges();

}

//Exception is thrown again because of the violation of primary key. it seems that RejectChanges() was unable to role back all the changes

//when an entity was not added through the table but through attachment to another entity that the data context is aware of.

db.SubmitChanges();

/**************************************************/

/* issue with foreign key of type nvarchar */

//the case that works...

Patient pat2 = new Patient(){Name = "Mr. Brown", DoctorFK = doc.Id, Age = 50, Enrolldate = DateTime.UtcNow};

db.Patients.Add(pat2);

db.SubmitChanges();

//the case that fails...

Doctor doc3 = new Doctor(){Name = "Mr. Jones", HospitalFK = hos.Name};

db.Doctors.Add(doc3);

//Exception: Cannot insert the value NULL into column 'hospitalFK', table 'DlinqDemo.dbo.doctor'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated

db.SubmitChanges();

/***************************************************/

/* issue with the Including() */

// the following line fails to compile: Error 1 The type arguments for method 'System.Data.DLinq.QueryExtensions.Including<T>(System.Data.DLinq.Query<T>, params System.Expressions.Expression<System.Query.Func<T,object>>[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. D:\My Documents\Visual Studio 2005\Projects\LINQConsoleApplication1\LINQConsoleApplication1\Program.cs 53 44 LINQConsoleApplication1

//var p = (from i in db.Patients select i).Including(i => i.Doctor.Including(j => j.Hospital));

/****************************************************/

/* issue with the Where operator */

//the case that works...

var p = from i in db.Patients where i.Enrolldate < (DateTime.UtcNow + TimeSpan.FromDays(1)) select i;

p.ToList();

//the case that fails... Runtime exception: Invalid cast from 'System.DateTime' to 'System.TimeSpan'

//i've also run into some other types of runtime exception associated with datetime/timespan related calculation in a where clause.

var p1 = from i in db.Patients where (i.Enrolldate - TimeSpan.FromDays(1)) < DateTime.UtcNow select i;

p1.ToList();

/***************************************************/

/* issue with data context caching... */

DlinqDemo db1 = new DlinqDemo(@"Data Source=localhost\sqlexpress;Initial Catalog=DlinqDemo;Integrated Security=True");

Patient pat3 = db1.Patients.First(i => i.Name == "Mr. Sick");

int age = pat.Age;

//modify the age through another data context

Patient pat4 = db.Patients.First(i => i.Name == "Mr. Sick");

pat4.Age += 1;

db.SubmitChanges();

//Not query is issued to the db... this is in memory

Patient pat5 = db1.Patients.First(i => i.Name == "Mr. Sick");

//assertion fails

Assert.IsTrue(age == pat5.Age);

//Query issued to the db...

var p2 = from i in db1.Patients where (i.Name == "Mr. Sick") select new { Age = i.Age};

//assertion succeeds

Assert.IsTrue(age == p2.First().Age);

}

}

}

/*script used to setup the db*/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[hospital]') AND type in (N'U'))

BEGIN

CREATE TABLE [dbo].[hospital](

[name] [nvarchar](50) NOT NULL,

CONSTRAINT [PK_hospital] PRIMARY KEY CLUSTERED

(

[name] ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

END

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[doctor]') AND type in (N'U'))

BEGIN

CREATE TABLE [dbo].[doctor](

[name] [nvarchar](50) NOT NULL,

[id] [int] IDENTITY(1,1) NOT NULL,

[hospitalFK] [nvarchar](50) NOT NULL,

CONSTRAINT [PK_doctor] PRIMARY KEY CLUSTERED

(

[id] ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

END

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[patient]') AND type in (N'U'))

BEGIN

CREATE TABLE [dbo].[patient](

[name] [nvarchar](50) NOT NULL,

[doctorFK] [int] NOT NULL,

[enrolldate] [datetime] NOT NULL,

[age] [int] NOT NULL,

CONSTRAINT [PK_patient] PRIMARY KEY CLUSTERED

(

[name] ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

END

GO

IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_doctor_hospital]') AND parent_object_id = OBJECT_ID(N'[dbo].[doctor]'))

ALTER TABLE [dbo].[doctor] WITH CHECK ADD CONSTRAINT [FK_doctor_hospital] FOREIGN KEY([hospitalFK])

REFERENCES [dbo].[hospital] ([name])

GO

IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_patient_doctor]') AND parent_object_id = OBJECT_ID(N'[dbo].[patient]'))

ALTER TABLE [dbo].[patient] WITH CHECK ADD CONSTRAINT [FK_patient_doctor] FOREIGN KEY([doctorFK])

REFERENCES [dbo].[doctor] ([id])




Answer this question

A few Dlinq bugs

  • jessj

    I agree with you on both comments related to foreign key and caching. There are debate over this two areas and it is why I would say it is the "current" design behaviour.

    By the way,

    The right behavior is to always return the cached data and provide a way to explicitly invalidate the cache.

    For your example, it cached the "patient" in the first query. In the second query, it is querying the "age" of the first patient.

    I understand we could find out the age of the patient from the paitent object we cached. But as I know, we are currently doing caching on entity level only.

    The right behavior is to always return the cached data and provide a way to explicitly invalidate the cache.

    Currently, you could invalidate the cache by doing something like "db = new DlinqDemo("XXX")"


  • Atilla

    For foreign key: Please don't fix the bug in the wrong way :) I think the desired behavior is that the foreign key field shoud take precedence when the reference has not been set.

    For data caching: hmm... it doesn't make sense. the behavior is in consistant. db1 should have the value of Age of "Mr. Sick" after the query Patient pat3 = db1.Patients.First(i => i.Name == "Mr. Sick"). If cache is always used, new query is not necessary for var p2 = from i in db1.Patients where (i.Name == "Mr. Sick") select new { Age = i.Age}; The right behavior is to always return the cached data and provide a way to explicitly invalidate the cache.



  • gurminder

    Thanks for the reply. Just two follow up questions:

    1. Foreign Key Issue - The problem was with nvarchar and not of foreign in general. The answer provided in http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=228964&SiteID=1 fails to provide an explanation. In claims that “if the reference does not match the foriegn-key values will overwrite your currently foreign-key values so that they match the reference. “ However, in the example that I provided:

    Patient pat2 = new Patient(){Name = "Mr. Brown", DoctorFK = doc.Id, Age = 50, Enrolldate = DateTime.UtcNow};

    db.Patients.Add(pat2);

    db.SubmitChanges();

    pat.Doctor is indeed null, but the record was successfully inserted into the DB. If you look at the query generated, Dlinq correctly specified doc.Id for the corresponding column in the insert statement. However if the foreign key is of nvarchar, Dlinq uses null in the insert statement.

    2. Data caching - could you elaborate on why

    Query is NOT issued for: Patient pat5 = db1.Patients.First(i => i.Name == "Mr. Sick");

    Query is issued for: var p2 = from i in db1.Patients where (i.Name == "Mr. Sick") select new { Age = i.Age};

    How does Dlinq decide when and when not to use the cached data



  • TaTas

    You are right, I am over look on the foreign key issue, I will log the issue.

    For the data caching problem, here is the explaination.

    Patient pat3 = db1.Patients.First(i => i.Name == "Mr. Sick");

    // Patient in db1 data context

    Patient pat4 = db.Patients.First(i => i.Name == "Mr. Sick");

    pat4.Age += 1;

    db.SubmitChanges();

    // Changing patient in db data context

    Patient pat5 = db1.Patients.First(i => i.Name == "Mr. Sick");

    // Query the first patient again from db1 data context and there is already one in there. No database query is needed. -- It will return the cached patient in db1 data context to you.

    var p2 = from i in db1.Patients where (i.Name == "Mr. Sick") select new { Age = i.Age};

    // Query the age of the first patient from db1 data context, the "patient" is cached but the "Age of the patient" is not. Query the database again to get the current data for you.

    //Note: For a patient, we could use the primary key to identify the object are the same, but for "Age", it is just an integer value.

    I hope this help


  • Ares01

    Thank you for your feedback.

    Here are the quick answer for the issues you find.

    • Rejected issue -- The issue had been logged
    • Foreign Key issue -- It is the current design behaviour

    For more information, please refer to http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=228964&SiteID=1

    • Including issue -- It is the current design behaviour. i.Doctor is not an EntitySet.

    For more information, you could refer to Object Loading sample. You could modify the eager loading like this.

    var custs = (
    from c in db.Customers
    where c.City == "Sao Paulo"
    select c )
    .Including(c => c.Orders.Including(d => d.OrderDetails));

    • DateTime and Timespace conversion issue -- The issue had been logged
    • Caching issue -- It is the current design behaviour.

    For more information, you could refer to Sample99 of the Dlinq 101 Samples.


  • A few Dlinq bugs