none
Replacing EntitySet<T> with Custom Container RRS feed

  • Question

  • From my past experience, I remember that it is possible to use POCO-classes instead of SQLMetal-generated source-code for LINQ to SQL entities and it is possible to replace associations of type EntitySet<TEntity> with List<TEntity>s, and associations of type EntityRef<TEntity>s with actual instances of TEntity.

    Seeing all the samples and blog posts regarding this subject and working with the feature myself, I concluded that it is actually possible to replace an EntitySet<TEntity> not only with List<TEntity> but with any type that implements IList<TEntity> or may be ICollection<TEntity>.

    Today, for some purpose, I wrote my own collection named EntityBag<TEntity> implementing IList<TEntity>, IList, and IListSource and started testing it. When I tried to use eager-loading feature of LINQ to SQL to fetch instances of entity type Foo with its related Bars, I faced the following InvalidCastException:


    Message: {"Could not convert from type 'System.Collections.Generic.List`1[MyApp.Bar]' to type MyApp.EntityBag`1[MyApp.Bar]'."}

    StackTrace:
       at System.Data.Linq.DBConvert.ChangeType(Object value, Type type)
       at Read_InternalLogicalResource(ObjectMaterializer`1 )
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
       at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
       at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)


    I tried almost every way like writing a custom TypeConverter  for my collection, writing an implicit cast operator from List<T> to EntityBag<T>, etc. to tell LINQ to SQL that it is possible to convert List<T> to my EntityBag<T>, but unfortunately, none of these methods solved my problem. 

     

    Later, considering that having a parameterless constructor would help LINQ to SQL construct my collection and populate it with the list of items, I added the parameterless constructor and interestingly faced a different exception:

    NotSupportedException: {"General collection materialization is not supported."}
    StackTrace:
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.GenerateConvertToType(Type actualType, Type expectedType)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.GenerateAssignValue(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.GenerateMemberAssignment(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.GenerateNew(SqlNew sn)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.Generate(SqlNode node, LocalBuilder locInstance)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.GenerateBody(ILGenerator generator, SqlExpression expression)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.CompileDynamicMethod(Generator gen, SqlExpression expression, Type elementType)
       at System.Data.Linq.SqlClient.ObjectReaderCompiler.Compile(SqlExpression expression, Type elementType)
       at System.Data.Linq.SqlClient.SqlProvider.GetReaderFactory(SqlNode node, Type elemType)
       at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
       at System.Data.Linq.DataQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
       at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
       at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

    This made me certain that what I would like to achieve is not currently possible with LINQ to SQL. Well! If my conclusion is right, then I'm wondering why LINQ to SQL team has made the decision not to support this feature which, in my opinion, is not hard to implement.

    Any ideas/suggestions/clarifications?

    Wednesday, May 14, 2008 3:50 AM

Answers

  • I don't think that this is a trivial task, as you might likely kill the support for Deferred Loading and Object Tracking. If EntitySet had implemented an interface and it had been used in the Linq instead of the concrete implementation, you could have been able to plug your own collection.

     

    BTW, why do you want to replace EntitySet ?

    Monday, June 16, 2008 5:30 AM

All replies

  • Hi, sorry nobody has answered your question.

     

    Did you get anywhere with using your own custom container?

     

    [)amien

    Tuesday, June 10, 2008 5:19 PM
    Moderator
  • Hi Damien,

    Sorry for answering with delay! No! I tried a lot to plug my custom container into LINQ to SQL without any success.

    Finally, Getting no answer from the forum, I tried to see what was going on in LINQ to SQL by inspecting the code of
    System.Data.Linq.SqlClient.ObjectReaderCompiler.Generator.GenerateConvertToType(Type actualType, Type expectedType) with Lutz Roeder's Reflector and found out that doing so is simply not supported in the current version of LINQ to SQL. This is the chunk of code that made me think so:

                if ((expectedType.IsClass && typeof(ICollection<>).MakeGenericType(new Type[] { elementType }).IsAssignableFrom(expectedType)) && ((expectedType.GetConstructor(Type.EmptyTypes) != null) && sequenceType.IsAssignableFrom(actualType)))
                {
                    throw Error.GeneralCollectionMaterializationNotSupported();
                }

    Do you confirm this? If so, Is there any plan to support custom containers in the future versions of LINQ to SQL?

    Saturday, June 14, 2008 1:21 PM
  • I don't think that this is a trivial task, as you might likely kill the support for Deferred Loading and Object Tracking. If EntitySet had implemented an interface and it had been used in the Linq instead of the concrete implementation, you could have been able to plug your own collection.

     

    BTW, why do you want to replace EntitySet ?

    Monday, June 16, 2008 5:30 AM