Entity collection constructor

Posts   
 
    
pilotboba
User
Posts: 434
Joined: 05-Aug-2005
# Posted on: 06-Apr-2006 00:05:36   

Frans,

I just had a question about some sample code you had in another thread:

EntityCollection<CustomerEntity> customers = new EntityCollection<CustomerEntity>( new CustomerEntityFactory() );

Since the Entitycollection is strongly typed (due to generics) is it really necessary to pass in the entity factory. Shouldn't the collection be able to figure out which factory to use based on the type?

If this was the case, the code could be simplified to:

EntityCollection<CustomerEntity> customers = new EntityCollection<CustomerEntity>();

Also, will v2.x require .Net 2.x, I assume so since you are using generics all over the place. Or are you still supporting 1.1 by generating non-generic strongly type collections?

BOb

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 06-Apr-2006 08:33:52   

pilotboba wrote:

Frans,

I just had a question about some sample code you had in another thread:

EntityCollection<CustomerEntity> customers = new EntityCollection<CustomerEntity>( new CustomerEntityFactory() );

Since the Entitycollection is strongly typed (due to generics) is it really necessary to pass in the entity factory. Shouldn't the collection be able to figure out which factory to use based on the type?

If this was the case, the code could be simplified to:

EntityCollection<CustomerEntity> customers = new EntityCollection<CustomerEntity>();

That could be done indeed. It needs a new method in the entity to be generated that returns the factory. I'll add that, it's a small thing. simple_smile

Also, will v2.x require .Net 2.x, I assume so since you are using generics all over the place. Or are you still supporting 1.1 by generating non-generic strongly type collections? BOb

You don't need .NET 2.0 for your code to run. The designer uses .NET 2.0 now, though. I have two runtime code bases: one for .NET 1.x and one for .NET 2.0. The one in .NET 2.0 uses generics, the one in .NET 1.x doesn't. So you can keep on using .NET 1.x and also it's not hard (but there will be some action necessary here and there but it's minor) to migrate your .NET 1.x project to v2 and keep on using .NET 1.x. You can also migrate to .NET 2.0, I've kept the EntityCollection() class for backwards compatibility (and also for design time databinding, which doesn't work with generics... )

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 23-Apr-2006 11:51:02   

I can't get this working... The problem is.. I need to create a new instance of the entitytype passed in, like: TEntity dummy = new TEntity();

though, this requires TEntity (the generic parameter) to have a where clause 'new()'. Sounds simple, though TEntity is 'EntityBase2', in the where clause. new() then can't be specified as teh compiler whines that EntityBase2 doesnt have an empty constructor (it does, but it's an abstract class so it won't work).

Frans Bouma | Lead developer LLBLGen Pro
Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 23-Apr-2006 13:56:42   

Otis wrote:

I can't get this working... The problem is.. I need to create a new instance of the entitytype passed in, like: TEntity dummy = new TEntity();

though, this requires TEntity (the generic parameter) to have a where clause 'new()'. Sounds simple, though TEntity is 'EntityBase2', in the where clause. new() then can't be specified as teh compiler whines that EntityBase2 doesnt have an empty constructor (it does, but it's an abstract class so it won't work).

If IEntity2 has all methods and properties that you need to call inside the entitycollection, then you can try declaring TEntity with IEntity2, New.

[Edit]

I am not sure; but may be you can declare TEntity with a where EntityBase2, New.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 24-Apr-2006 10:55:04   

Rogelio wrote:

Otis wrote:

I can't get this working... The problem is.. I need to create a new instance of the entitytype passed in, like: TEntity dummy = new TEntity();

though, this requires TEntity (the generic parameter) to have a where clause 'new()'. Sounds simple, though TEntity is 'EntityBase2', in the where clause. new() then can't be specified as teh compiler whines that EntityBase2 doesnt have an empty constructor (it does, but it's an abstract class so it won't work).

If IEntity2 has all methods and properties that you need to call inside the entitycollection, then you can try declaring TEntity with IEntity2, New.

I could define an internal interface on EntityBase2 and implement it explicitly, but that's not going to work either in this case... disappointed And a static method on EntityBase2 is also not working, as it won't be able to retrieve the subclass' factory...

[Edit] I am not sure; but may be you can declare TEntity with a where EntityBase2, New.

That's what I tried but that doesn't work, the compiler tells me it needs an empty constructor but that's not going to work on an abstract class (to which I added an empty constructor to no avail..)

Frans Bouma | Lead developer LLBLGen Pro
Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 24-Apr-2006 13:47:36   

Otis wrote:

Rogelio wrote:

Otis wrote:

I can't get this working... The problem is.. I need to create a new instance of the entitytype passed in, like: TEntity dummy = new TEntity();

though, this requires TEntity (the generic parameter) to have a where clause 'new()'. Sounds simple, though TEntity is 'EntityBase2', in the where clause. new() then can't be specified as teh compiler whines that EntityBase2 doesnt have an empty constructor (it does, but it's an abstract class so it won't work).

If IEntity2 has all methods and properties that you need to call inside the entitycollection, then you can try declaring TEntity with IEntity2, New.

I could define an internal interface on EntityBase2 and implement it explicitly, but that's not going to work either in this case... disappointed And a static method on EntityBase2 is also not working, as it won't be able to retrieve the subclass' factory...

[Edit] I am not sure; but may be you can declare TEntity with a where EntityBase2, New.

That's what I tried but that doesn't work, the compiler tells me it needs an empty constructor but that's not going to work on an abstract class (to which I added an empty constructor to no avail..)

Frans,

I did the following test:



Public Class Class1(Of T As {ClassBase, New})
    Private test As T = New T

    Public Sub DoSomething()

        test.DoABC()

    End Sub

End Class

Public MustInherit Class ClassBase
    Public Sub New()
    End Sub

    Public Overridable Sub DoABC()
    End Sub
End Class

Public Class Derive
    Inherits ClassBase

    Public Sub New()
        MyBase.New()
    End Sub

    Public Overrides Sub DoABC()

    End Sub

End Class



And I was able to create an instance of Class1(of Derive)

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 24-Apr-2006 14:37:38   

Hmm... I'll see what I did wrong and get back to you. I also thought it should be ok, but it didn't. Perhaps C# is more restrictive in this, I'll check it out.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 24-Apr-2006 18:27:56   

Ok, here's the problem:


    public abstract class EntityBase2
    {
        public EntityBase2()
        {
        }
    }


    public class CustomerEntity : EntityBase2
    {
        private string _name;

        public CustomerEntity()
        {
            _name = string.Empty;
        }
    }


    public abstract class EntityCollectionBase2<T>
        where T:EntityBase2, new()
    {
        public EntityCollectionBase2()
        {
            T t = new T();
        }
    }


    public class EntityCollection : EntityCollectionBase2<EntityBase2>
^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        public EntityCollection()
        {
        }
    }

at the line ^^^^^^^^^, the compiler gives the error: "The type 'CS20Tests.EntityBase2' must have a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'CS20Tests.EntityCollectionBase2<T>'"

The class EntityCollection is there for backwards compatibility. The thing is, I can only define it as a subclass of EntityCollectionBase2<EntityBase2>, because C# and VB.NET don't support covariance in generics. This for example isn't true: List<string> strings = new List<string>(); List<object> asObject = (List<object>)strings;

Without that class, it would work, though with that class it doesn't. I can't define EntityCollection as EntityCollectionBase2<IEntity2>, because the type T has to implement EntityBase2 (or any other internal interface if I implement such an interface).

Frans Bouma | Lead developer LLBLGen Pro
Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 24-Apr-2006 19:13:58   

Now I can see where is the problem...

pilotboba
User
Posts: 434
Joined: 05-Aug-2005
# Posted on: 24-Apr-2006 19:32:36   

Otis wrote:

The class EntityCollection is there for backwards compatibility. The thing is, I can only define it as a subclass of EntityCollectionBase2<EntityBase2>, because C# and VB.NET don't support covariance in generics. This for example isn't true: List<string> strings = new List<string>(); List<object> asObject = (List<object>)strings;

Without that class, it would work, though with that class it doesn't. I can't define EntityCollection as EntityCollectionBase2<IEntity2>, because the type T has to implement EntityBase2 (or any other internal interface if I implement such an interface).

Isn't this what Generic constraints are for. Did you watch the dnrtv.com episode on generics. I think he discusses this issue, such that...

Dog derives from Animal but List<Dog> does not derive from List<Animal>. This is the Liskov Substitution Priciple. But, you still want to create a method that takes List<Animal> or a list of anything that derives from Animal.

I don't remember the exact syntax, but I think the resolution was to do something like:

Constructor<T>( List<T> ) WHERE T : Anmial

I think this would solve your issue.

BOb

EDIT: Ah, ok this won't work. I think you have to derive a generic type from a generic type.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 25-Apr-2006 08:52:00   

Isn't this what Generic constraints are for. Did you watch the dnrtv.com episode on generics. I think he discusses this issue, such that...

Dog derives from Animal but List<Dog> does not derive from List<Animal>. This is the Liskov Substitution Priciple. But, you still want to create a method that takes List<Animal> or a list of anything that derives from Animal.

No, that's not it. Constraints are for the compiler to make decisions if the code written with the generic type is always working at runtime. simple_smile List<Dog> doesn't derive from List<Animal> as that would require covariance in the language. Dog[] does derive from Animal[], although that is effectively the same. Covariance is a feature of the CLR but not of C# or VB.NET.

Frans Bouma | Lead developer LLBLGen Pro
pilotboba
User
Posts: 434
Joined: 05-Aug-2005
# Posted on: 26-Apr-2006 19:59:38   

Otis wrote:

Ok, here's the problem:


    public abstract class EntityBase2
    {
        public EntityBase2()
        {
        }
    }


    public class CustomerEntity : EntityBase2
    {
        private string _name;

        public CustomerEntity()
        {
            _name = string.Empty;
        }
    }


    public abstract class EntityCollectionBase2<T>
        where T:EntityBase2, new()
    {
        public EntityCollectionBase2()
        {
            T t = new T();
        }
    }


    public class EntityCollection : EntityCollectionBase2<EntityBase2>
^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        public EntityCollection()
        {
        }
    }

You get an error because EntityBase2 is an abstract class. In order to instantitate EntityCollectionBase2<EntityBase2>, EntityBase2 can't be abstract. I also don't think you would code this anyway... wouldn't you generate:

Public Class EntityCollection : EntityCollectionBase2<CustomerEntity>

???

You could have EntityBase2 impelement IEntityBase2, change the constraints on your generic to WHERE T : IEntityBase2. Of course you still have to use the type above when you subclass. Not sure that you need to introduce an empty interface.

I guess I am not sure what you are trying to do. Why can't the EntityCollection non-generic stay as is for backward compatiblity?

BOb

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 27-Apr-2006 10:03:12   

pilotboba wrote:

Otis wrote:

Ok, here's the problem:


    public abstract class EntityBase2
    {
        public EntityBase2()
        {
        }
    }


    public class CustomerEntity : EntityBase2
    {
        private string _name;

        public CustomerEntity()
        {
            _name = string.Empty;
        }
    }


    public abstract class EntityCollectionBase2<T>
        where T:EntityBase2, new()
    {
        public EntityCollectionBase2()
        {
            T t = new T();
        }
    }


    public class EntityCollection : EntityCollectionBase2<EntityBase2>
^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        public EntityCollection()
        {
        }
    }

You get an error because EntityBase2 is an abstract class. In order to instantitate EntityCollectionBase2<EntityBase2>, EntityBase2 can't be abstract. I also don't think you would code this anyway... wouldn't you generate:

Public Class EntityCollection : EntityCollectionBase2<CustomerEntity>

???

The problem is that that EntityCollection can only contain CustomerEntity instances, and I need an entitycollection class which can contain any entity instance, as it is today.

You could have EntityBase2 impelement IEntityBase2, change the constraints on your generic to WHERE T : IEntityBase2. Of course you still have to use the type above when you subclass. Not sure that you need to introduce an empty interface.

I guess I am not sure what you are trying to do. Why can't the EntityCollection non-generic stay as is for backward compatiblity? BOb

The EntityCollection class is only for backwards compatibility wink . It's currently part of the ormsupportclasses but today I'll move it back to the generated code as it will otherwise break all those winforms which have an entitycollection on top of it.

In 1.0.2005.1, EntityCollection simply derives from EntityCollectionBase2 which derives from CollectionBase. In v2, I have a general collectioncore<T> class, which is the base for both EntityCollectionBase and EntityCollectionBase2. The generic EntityCollection<T> derives from EntityCollectionBase2<T>, however for backwards compatibility I need a non-generic collection class. Also for design time databinding I need a non-generic collection class, because design time databinding doesn't work with generics (in a way understandable, but still odd). And because it has to contain any entity, it can only be EntityCollection : EntityCollectionBase2<EntityBase2> or EntityCollection: EntityCollectionBase2<IEntity2>. The latter won't compile because the collection requires EntityBase2 internally, and the former will compile but then I can't create a new instance.

So the code you write today: EntityCollection customers = new EntityCollection(new CustomerEntityFactory()); still works, though it's not a generic collection, so this won't compile: CustomerEntity c = customers[0];

And because C# doesn't support covariance, this also won't compile: EntityCollection customers = new EntityCollection<CustomerEntity>(new CustomerEntityFactory());

even though CustomerEntity implements EntityBase2.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 21-Jul-2006 15:20:42   

Frans,

I wish I had either (a) Read the docs... or (b) seen this thread BEFORE I removed all the "new MyEntityFactory()" calls from my new EntityCollection<MyEntity>() constructors... My own fault I know... flushed

Was this ever solved?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 21-Jul-2006 16:15:15   

No, I couldn't find a solution for this, as all solutions I could think of didn't result in compilable code disappointed .

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 21-Jul-2006 17:56:18   

Otis wrote:

No, I couldn't find a solution for this, as all solutions I could think of didn't result in compilable code disappointed .

Yes I tried it myself... as I thought it was going to be simple at first. disappointed

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 22-Jul-2006 19:43:00   

Otis wrote:

No, I couldn't find a solution for this, as all solutions I could think of didn't result in compilable code disappointed .

Ok I have a solution... simple_smile and I've sent you a VS.NET sample project.

Essentially you need to generate a new static class called "EntityFactoryFactory" simple_smile


public static class EntityFactoryFactory
{
    public static IEntityFactory2 CreateFactory(Type entityType)
    {
        switch (entityType.ToString())
        {
            case "<NAMESPACE_GOES_HERE>.CustomerEntity":
                return new CustomerEntityFactory();
            ...
            default:
                throw new NotSupportedException();
        }
    }
}

which is called from the EntityCollection<T> class like:


public class EntityCollection<T> where T : EntityBase2, IEntity2
{
    public IEntityFactory2 _entityFactory;

    public EntityCollection()
    {
        _entityFactory = EntityFactoryFactory.CreateFactory(typeof(T));
    }
}

It's not the most elegant... but it works. smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 22-Jul-2006 20:59:54   

Cool! simple_smile I'll check it out. I received the email. Thanks! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
wojo
User
Posts: 69
Joined: 10-Mar-2004
# Posted on: 11-Aug-2006 16:57:59   

Ah, I just ran into this myself and wondered why it couldn't be done automatically using generics, too.

Is the fix mentioned above going to make it in? It'd sure be nice to not have to specify the entity factory every time like we had to in 1.0 smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 11-Aug-2006 18:10:57   

I decided not to add the code to the core code. However as it's solely a template change, and EntityCollection is a very small class, you could change the template yourself a bit simple_smile

Frans Bouma | Lead developer LLBLGen Pro
wojo
User
Posts: 69
Joined: 10-Mar-2004
# Posted on: 11-Aug-2006 18:45:54   

Fair enough! Thanks for the answer Frans!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 16-Aug-2006 21:34:45   

I fixed it! smile

Incredibly simple. I was reflectoring ADO.NET vNext's code and there it was... so simple: (in EntityCollectionBase2<T>'s empty constructor


Type t = typeof(T);
if(!t.IsAbstract)
{
    T dummy = Activator.CreateInstance(t) as T;
    //  and then I have to call a new internal method to call an entity's CreateEntityFactory method: 
    _entityFactoryToUse = dummy.CallCreateEntityFactory();
}

done simple_smile

Will roll this into the code in the next build.

Of course, the non-generic collections still need a factory as they use the abstract EntityBase2 type.

Frans Bouma | Lead developer LLBLGen Pro
wojo
User
Posts: 69
Joined: 10-Mar-2004
# Posted on: 16-Aug-2006 22:21:17   

Nice! So many tricks to learn in .NET, so little time! simple_smile