Support for Interfaces in Associations

Hi, I am trying to implemente Aurelius and the Delphi Spring Framework (spring4d) and trying to adhere as much as I can to only use interfaces (specially in my GUI code) however I found that Aurelius doesn´t seem to like my use of interfaces specially in the associations. For example, let suppose we have the following interfaces and classes:



IInvoiceItem = Interface
    [GUID]
    procedure setDescription(Value: String);
    function getDescription : String;
    property Description : String read getDescription write setDescription;
    procedure setPrice(Value: Currency);
    function getPrice : Currency;
    property Price: Currency read getPrice write setPrice;
end;


IInvoice = Interface
   [GUID]
   function GetTotal : Currency;
   property Total: Currency getTotal;
   property getItems : TList<IInvoiceItem>;
   property Items : TList<IInvoiceItem> read getItems;
end;


And have the following class implementations:


[Entity]

[Table('InvoiceItems')]
[Sequence('SEQ_Items')]
[Id('FID', TIdGenerator.IdentityOrSequence)]
TInvoiceItem = class(TInterfacedObject, IInvoiceItem)
private
     ...Aurelius [Columns] ...		
public
    .. implementation of Interface methods ..
end;



[Entity]

[Table('Invoice')]
[Sequence('SEQ_Invoice')]
[Id('FID', TIdGenerator.IdentityOrSequence)]
TInvoice = class(TInterfacedObject, IInvoice)
private
     [ManyValuedAssociation([], CascadeTypeAll)]
      [ForeignJoinColumn('id_turno', [TColumnProp.Required])]
      fItems: TList<TInvoiceItem>; // As Aurelius expects it, works Ok but I "require" a specific implementation of TInvoiceItem
      //fItems : TList<IInvoiceItem>; <--- As I expect it to work, Aurelius fails saying its an invalid association
public
    .. implementation of Interface methods ..
end;


Is there a way for Aurelius to support associations using Interfaces? is it planned? or just not plain possible? 

Thanks

Unfortunately this is not possible in current version. We would have to do a better investigation in future to see if and how this would be supported, especially because Aurelius manages the object instances, so when an object is loaded, an instance is created, I wonder what object is expected to be created and we are talking about interfaces not objects.

Ok, thank you Wagner, I know there has to be "someone" that needs to create the class instance. It would be great if this becomes a feature making easier to change implementations. 

I've been having the same problem. In Hibernate this is solved through the <any> configuration. The trick here is to not only store the ID but also the classname of the ID. That means that we would need an extension on the [ForeignJoinColumn] or an extra Attribute The ColumnName in itself is not sufficient. You would need the classname as well. Support for this of course has consequences for the current implementation. Implementations of Proxy and Lazy will be affected.


nhibernate


Another way could be to specify these 2 fields to be persisted (ForeignID and ClassName) and declare the interfaced property to have both these properties so the can be retrieved at runtime and then store in the 2 fields. You can then re-created the interfaced property using both fields and RTTI. Aurelius can currently only store a composition association, but no aggregation association, thus you need to simulate it. In an aggregation you only store the reference to the object, never the object itself. When the object is declared as an  interface then you would need to store the classname as well in order to recreate the object.

Not sure how this related to the topic? I think the original poster just wants to use interfaces as entity references, instead of objects/classes.

I apologize, I see now that although it's not specific about interfaces, <any> mapping can be used for such scenarios. The problem is that querying is not possible (or viable) and it should only be used for corner case scenarios. In the case of the original question, my guess is that Luiz wants to use interfaces all over his mapping. So it will destroy any querying mechanism in Aurelius. 


It also doesn't solve the remaining problems:
a) How to manage life of objects
b) How to "save" or "update" an interface (given we don't know its class)?

Hi,

a) keep references of interfaced objects you want to keep / prevent from destroying. Managing the life of objects is easier with interfaces than with plain objects because you have the possibility to hold the reference.

b) there should be an global master object like TAureliusInterfacedObject instead of TObject. And as such a master interface that all other object interfaces inherid from. Then the information needed for basic operations can be stored with it. Makes it a little bit heavier memory wise but mutch more flexible. Aurelius could be both object based or interface based (i prefer the latter). The developer should decide at the moment of exporting to pas file to use TObjects or TAureliusOInterfacedObjects. Global aurelius functions can be overloaded to accept TObject or IAureliusObjectInterface.


a) Yes, but mixing interfaces and objects in Delphi is a nightmare and should be avoided. That's the problem here.


b) How TAureliusInterfacedObject would help in this case? Nevertheless, requiring all entity objects to inherit from a specific class breaks one of the beauties of Aurelius which is the fact it works with plain Delphi classes and plain Delphi types, making it very flexible to be used with any type of object. It also breaks a little the purpose of interfaces, since one of interesting thing about interfaces is exactly the fact you can decouple your application and don't care about how the object is implemented or which class it inherits from.