Hi Atsushi,
in theory, DbLinq should only use its own EntitySet<> and EntityRef<>. If a
non-DbLinq class is present somewhere in test code, this is a mistake.
DbLinq probably still supports in the core a bit of MS EntitySet and Ref to
keep a bit of compatibility, but we may also drop those parts in the future.
Regarding eager loading, nothing appears to be in progress (I haven't heard
from Pablo for a while, by the way). I can develop my architecture idea
about this if you are interested.
Anyway, the following remaining tasks are related and should be solved
together:
- complex subexpressions
- eager and lazy loading (lazy loading currently works, but I'm not sure it
fits in the global idea)
However I haven't been giving DbLinq much time last month (apparently I'm
not the only one), so all of this is in standby mode.
Pascal.
On Thu, Nov 27, 2008 at 10:17, Atsushi Eno <[EMAIL PROTECTED]> wrote:
> Hello,
>
> Now I'm diving into this DataLoadOptions land with a patch.
>
> Basically, the patch adds support for validation of LoadWith() argument
> that should 1) allow only property or field access and 2) reject cyclic
> loading. They are with tests and I think this part is ready to get
> committed. (Note that it does not implement the actual eager loading.)
>
> A problem that I found during this hack was that there are two
> EntitySet<T> and EntityRef<T>: one in DbLinq.Data.Linq and another in
> System.Data.Linq.
>
> To my understanding, this kind of conflicts are usually resolved as
>
> - DbLinq.Data.Linq.EntitySet<T> in non-strict mode and
> - System.Data.Linq.EntitySet<T> in strict mode.
>
> But what I've found in running Test_NUnit_MsSql was that
> System.Data.Linq.EntitySet<T> was used in non-strict mode. Hence I had
> to add dirty #if !MONO_STRICT block in DataLoadOptions.cs.
>
> Is this kind of complexity expected? Or is it some kind of bug that
> System.Data.Linq.EntitySet<T> is populated in non-strict mode?
>
> And another question - is there any ongoing effort on eager loading
> i.e. inner join and left join? ;)
>
> Atsushi Eno
>
> >
>
> Index: tests/Test_NUnit/DataLoadOptions_Test.cs
> ===================================================================
> --- tests/Test_NUnit/DataLoadOptions_Test.cs (revision 0)
> +++ tests/Test_NUnit/DataLoadOptions_Test.cs (revision 0)
> @@ -0,0 +1,134 @@
> +using System;
> +using System.Collections.Generic;
> +using System.Linq;
> +using System.Text;
> +using NUnit.Framework;
> +using Test_NUnit;
> +
> +#if !MONO_STRICT
> +using nwind;
> +using DbLinq.Data.Linq;
> +#else
> +using MsNorthwind;
> +using System.Data.Linq;
> +#endif
> +
> +#if MONO_STRICT
> +namespace MsNorthwind
> +#else
> +namespace nwind
> +#endif
> +{
> + public partial class Customer
> + {
> + public object ExtraneousMethod()
> + {
> + return null;
> + }
> + }
> +}
> +
> +#if MYSQL
> + namespace Test_NUnit_MySql
> +#elif ORACLE
> +#if ODP
> + namespace Test_NUnit_OracleODP
> +#else
> + namespace Test_NUnit_Oracle
> +#endif
> +#elif POSTGRES
> +namespace Test_NUnit_PostgreSql
> +#elif SQLITE
> + namespace Test_NUnit_Sqlite
> +#elif INGRES
> + namespace Test_NUnit_Ingres
> +#elif MSSQL
> +#if MONO_STRICT
> +namespace Test_NUnit_MsSql_Strict
> +#else
> + namespace Test_NUnit_MsSql
> +#endif
> +#elif FIREBIRD
> + namespace Test_NUnit_Firebird
> +#else
> + #error unknown target
> +#endif
> +{
> + [TestFixture]
> + public class DataLoadOptions_Test : TestBase
> + {
> + static object ThrowException()
> + {
> + throw new ApplicationException();
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadExpression1()
> + {
> + new DataLoadOptions().LoadWith<Customer>(cc =>
> cc.ExtraneousMethod());
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadExpression2()
> + {
> + new DataLoadOptions().LoadWith<Customer>(cc => 1);
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadExpression3()
> + {
> + new DataLoadOptions().LoadWith<Customer>(cc =>
> ThrowException());
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadExpression4()
> + {
> + new DataLoadOptions().LoadWith<Customer>(cc =>
> cc.Orders.Select(o => o));
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadExpression5()
> + {
> + new DataLoadOptions().LoadWith<Order> (o =>
> o.Customer.Orders);
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadCycles1()
> + {
> + var lo = new DataLoadOptions();
> + lo.LoadWith<Customer>(c => c.Orders);
> + lo.LoadWith<Order>(o => o.Customer);
> + }
> +
> + [Test]
> + [ExpectedException(typeof(InvalidOperationException))]
> + public void LoadWith_BadCycles2()
> + {
> + var lo = new DataLoadOptions();
> + lo.LoadWith<Order>(o => o.Customer);
> + lo.LoadWith<Customer>(c => c.Orders);
> + }
> +
> + [Test]
> + public void LoadWith_Good1()
> + {
> + var lo = new DataLoadOptions();
> + lo.LoadWith<Customer>(c => c.Orders);
> + lo.LoadWith<Order>(o => o.Employee);
> + }
> +
> + [Test]
> + public void LoadWith_Good2()
> + {
> + var lo = new DataLoadOptions();
> + lo.LoadWith<Order>(o => o.Employee);
> + lo.LoadWith<Customer>(c => c.Orders);
> + }
> + }
> +}
> Index: tests/Test_NUnit/Test_NUnit_MsSql.csproj
> ===================================================================
> --- tests/Test_NUnit/Test_NUnit_MsSql.csproj (revision 960)
> +++ tests/Test_NUnit/Test_NUnit_MsSql.csproj (working copy)
> @@ -68,6 +68,7 @@
> </Compile>
> <Compile Include="Attach.cs" />
> <Compile Include="CompositePK_Test.cs" />
> + <Compile Include="DataLoadOptions_Test.cs" />
> <Compile Include="ReadTests_AnyCountFirst.cs" />
> <Compile Include="ReadTests_Conversions.cs" />
> <Compile Include="ReadTests_DateTimeFunctions.cs" />
> Index: tests/Test_NUnit/ReadTests_EntitySet.cs
> ===================================================================
> --- tests/Test_NUnit/ReadTests_EntitySet.cs (revision 960)
> +++ tests/Test_NUnit/ReadTests_EntitySet.cs (working copy)
> @@ -288,8 +288,8 @@
> db.LoadOptions = loadoptions;
>
> var customer = db.Customers.First();
> - Assert.IsFalse(customer.Orders.IsDeferred);
> - Assert.IsTrue(customer.Orders.HasLoadedOrAssignedValues);
> + Assert.IsFalse(customer.Orders.IsDeferred, "#1");
> + Assert.IsTrue(customer.Orders.HasLoadedOrAssignedValues,
> "#2");
> }
>
> [Test]
> Index: tests/Test_NUnit/Test_NUnit_MsSql_Strict.csproj
> ===================================================================
> --- tests/Test_NUnit/Test_NUnit_MsSql_Strict.csproj (revision 960)
> +++ tests/Test_NUnit/Test_NUnit_MsSql_Strict.csproj (working copy)
> @@ -68,6 +68,7 @@
> </Compile>
> <Compile Include="Attach.cs" />
> <Compile Include="CompositePK_Test.cs" />
> + <Compile Include="DataLoadOptions_Test.cs" />
> <Compile Include="ReadTest_Subquery.cs" />
> <Compile Include="DynamicLinqTest.cs" />
> <Compile Include="ExecuteCommand_Test.cs" />
> Index: src/DbLinq/Data/Linq/DataLoadOptions.cs
> ===================================================================
> --- src/DbLinq/Data/Linq/DataLoadOptions.cs (revision 960)
> +++ src/DbLinq/Data/Linq/DataLoadOptions.cs (working copy)
> @@ -100,10 +100,45 @@
> {
> // TODO: ensure we have an EntitySet<>
> var memberInfo = ReflectionUtility.GetMemberInfo(expression);
> + if (memberInfo == null)
> + throw new InvalidOperationException("The argument
> expression must be a property access or a field access where the target
> object is the parameter");
> if (!eagerLoading.Contains(memberInfo))
> + {
> + VerifyMemberAccessCycles(memberInfo);
> eagerLoading.Add(memberInfo);
> + }
> }
>
> + private void VerifyMemberAccessCycles(MemberInfo member)
> + {
> + var mt = GetMemberEntityType (member);
> + var d = member.DeclaringType;
> + foreach (var m in eagerLoading)
> + {
> + if (m.DeclaringType == mt && GetMemberEntityType (m) == d)
> + throw new InvalidOperationException("Illegal cycles
> are detected in the argument expression among other eager-loading
> expressions");
> + }
> + }
> +
> + private Type GetMemberEntityType(MemberInfo member)
> + {
> + var mt = member.GetMemberType();
> + if (mt.IsGenericType)
> + {
> +#if !MONO_STRICT
> + if (mt.GetGenericTypeDefinition() == typeof(EntitySet<>))
> // DbLinq.Data.Linq.EntitySet
> + mt = mt.GetGenericArguments()[0];
> + else if (mt.GetGenericTypeDefinition() ==
> typeof(EntityRef<>)) // DbLinq.Data.Linq.EntityRef
> + mt = mt.GetGenericArguments()[0];
> +#endif
> + if (mt.GetGenericTypeDefinition() ==
> typeof(System.Data.Linq.EntitySet<>))
> + mt = mt.GetGenericArguments()[0];
> + else if (mt.GetGenericTypeDefinition() ==
> typeof(System.Data.Linq.EntityRef<>))
> + mt = mt.GetGenericArguments()[0];
> + }
> + return mt;
> + }
> +
> /// <summary>
> /// Tells if we do eager or lazy loading
> /// </summary>
> Index: src/DbLinq/Data/Linq/EntityRef.cs
> ===================================================================
> --- src/DbLinq/Data/Linq/EntityRef.cs (revision 960)
> +++ src/DbLinq/Data/Linq/EntityRef.cs (working copy)
> @@ -54,7 +54,6 @@
> hasLoadedOrAssignedValue = true;
> }
>
> - [DbLinqToDo]
> public EntityRef(IEnumerable<TEntity> source)
> {
> this.source = source;
> @@ -64,9 +63,14 @@
>
> public EntityRef(EntityRef<TEntity> entityRef)
> {
> - this.source = null;
> - this.entity = entityRef.Entity;
> - hasLoadedOrAssignedValue = true;
> + this.entity = entityRef.entity;
> + if (entityRef.entity == null && entityRef.source is
> ICloneable)
> + {
> + source =
> (IEnumerable<TEntity>)((ICloneable)entityRef.source).Clone();
> + }
> + else
> + source = null;
> + hasLoadedOrAssignedValue = entityRef.hasLoadedOrAssignedValue;
> }
>
> public TEntity Entity
> Index: src/DbLinq/Util/ReflectionUtility.cs
> ===================================================================
> --- src/DbLinq/Util/ReflectionUtility.cs (revision 960)
> +++ src/DbLinq/Util/ReflectionUtility.cs (working copy)
> @@ -66,34 +66,39 @@
> }
>
> /// <summary>
> - /// Extracts a MemberInfo with more or less digging
> + /// Returns MemberInfo specified in the lambda, optional parameter
> expression constraint.
> /// </summary>
> /// <param name="expression"></param>
> /// <returns></returns>
> - private static MemberInfo GetMemberInfo(Expression expression)
> + public static MemberInfo GetMemberInfo(LambdaExpression
> expression)
> {
> - switch (expression.NodeType)
> - {
> - // the ReflectionUtility Get** methods return the value as a
> object. If the value is a struct, we get a cast,
> - // that we must unwrap
> - case ExpressionType.Convert:
> - case ExpressionType.ConvertChecked:
> - return
> GetMemberInfo(((UnaryExpression)expression).Operand);
> - case ExpressionType.MemberAccess:
> - return ((MemberExpression)expression).Member;
> - default:
> - return null;
> - }
> + var paramExpr = expression.Parameters.Count == 1 ?
> expression.Parameters[0] : null;
> + return GetMemberInfo(paramExpr, expression.Body);
> }
>
> /// <summary>
> - /// Returns MemberInfo specified in the lambda
> + /// Returns MemberInfo specified in the lambda, optional parameter
> expression constraint.
> /// </summary>
> /// <param name="lambdaExpression"></param>
> /// <returns></returns>
> - public static MemberInfo GetMemberInfo(LambdaExpression
> lambdaExpression)
> + private static MemberInfo GetMemberInfo(Expression
> optionalParamExpr, Expression expression)
> {
> - return GetMemberInfo(lambdaExpression.Body);
> + switch (expression.NodeType)
> + {
> + // the ReflectionUtility Get** methods return the value as
> a object. If the value is a struct, we get a cast,
> + // that we must unwrap
> + case ExpressionType.Convert:
> + case ExpressionType.ConvertChecked:
> + return GetMemberInfo(optionalParamExpr,
> ((UnaryExpression)expression).Operand);
> + case ExpressionType.MemberAccess:
> + var me = (MemberExpression)expression;
> + if (optionalParamExpr == null || me.Expression ==
> optionalParamExpr)
> + return me.Member;
> + else
> + return null;
> + default:
> + return null;
> + }
> }
>
> /// <summary>
>
>
--
Pascal.
jabber/gtalk: [EMAIL PROTECTED]
msn: [EMAIL PROTECTED]
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"DbLinq" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/dblinq?hl=en
-~----------~----~----~----~------~----~------~--~---