Associations without Foreign-Key constraints

Is there any way to create Associations without fk-constraints?

I would like to implement a single table (and class) for translations.
It should look like this:

id, version, language, value, parent_id

some other tables would just implement Many-Valued-Associations on this entity.
Because of generated foreign-keys I'm not able to use the MappedBy Parameter from your Attribute ManyValuedAssociation.

This should work, because all parent_ids are generated by SmartGuid.

It's not possible.

Why is that?

otherwise, I could have used the same Field as MappedBy-Parameter.

Why can't you use the same field?

I mean something like that:

[AbstractEntity]
[Id('FId', TIdGenerator.SmartGuid)]
TVersionedEntity = class
private
  FId: TGUID;
  FVersion: Int64;
end;

[Entity]
TLangEntity = class(TVersionedEntity)
private
  FLanguage: string;
  FValue: string;
  FEntityId: TGUID;
end;

[Entity]
TTranslateableEntityOne = class(TVersionedEntity)
private
  [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAllRemoveOrphan, 'FEntityId')]
  FLanguageList: proxy<TList<TLangEntity>>;
end;

[Entity]
TTranslateableEntityTwo = class(TVersionedEntity)
private
  [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAllRemoveOrphan, 'FEntityId')]
  FLanguageList: proxy<TList<TLangEntity>>;
end;

I didn't gave it a try, but I consider that FEntityId should be correct type, meaning for TTranslateableEntityOne it should be TTranslateableEntityOne, and so on.
Also I consider that TTranslateableEntityOne and TTranslateableEntityTwo would generate foreign keys on FEntityId...

Correct. You need to have FEntityOne: TTranslateableEntityOne and FEntityTwo: TTranslateableEntityTwo fields in TLangEntity if you want both translatable class to have lists of TLangEntity.

Of course, you can also create an inheritance and have FEntityId: TTranslateableAncestor and then declare the list in an ancestor class, and inherit the translatable one and two classes from the base ancestor.

Now I did something like that, and I'm aware, that this is not, what you told me...:

[AbstractEntity]
[Id('FId', TIdGenerator.SmartGuid)]
TVersionedEntity = class
private
  FId: TGUID;
  FVersion: Int64;
end;

[Entity]
TLanguageBase= class(TVersionedEntity)
private
  FLanguage: string;
end;

[Entity]
TLanguage<TRef: class; TVal> = class abstract(TLanguageBase)
private
  FParentEntity: Proxy<TRef>;
  FValue: TVal;

  function GetParentEntity: TRef;
  procedure SetParentEntity(const Value: TRef);
public
  property ParentEntity: TRef read GetParentEntity write SetParentEntity;
  property Value: TVal read FValue write FValue;
end;

[Entity]
TTranslateableEntity<TLangEntity: TLanguageBase> = class abstract
strict private
  [ManyValuedAssociation([TAssociationProp.Lazy], CascadeTypeAllRemoveOrphan, 'FParentEntity'),]
  FLanguage: Proxy<TList<TLangEntity>>;
public
  constructor Create; override;
  destructor Destroy; override;
end;

  TTest = class;
  [Entity]
  TTestLang = class(TLanguage<TTest, TBlob>)
  end;

  [Entity]
  TTest = class(TTranslateableEntity<TTestLang>)
  end;

This works fine, except that ManyValuedAssociation will raise EInvalidMappedByReference.
Debugging this I see, that this occurs here:

function TMappingAttributeStrategy.LoadColumnsFromManyValuedAssociationMember(RttiInfo: TRttiOptimization): TArray<TColumn>;
...
        if not Clazz.InheritsFrom(MappedMemberRealType.AsInstance.MetaclassType) then
          raise EInvalidMappedByReference.Create(Clazz, RttiInfo.MemberName);
...

Because Clazz is TTranslateableEntity<....TTestLang >
MappedMemberRealType.AsInstance.MetaclassType is correctly set to TTest.

Is there anything I (or you) could change, that this will work and not break your current implementation?

Are you able to send me a small project reproducing the issue so I can check better what's going on? Probably it's something to do with RTTI and generics.

But note that you will need small teaks in your code. You can't inherit an entity from another entity without creating an explicit inheritance hierarchy using [Inheritance] attribute.

So entities like TLanguageBase, TLanguage<TRef, TVal> and TTranslateableEntity<TLangEntity> should be abstract entities.

I will try to build a sample project.

You are absolutely right, that TLanguageBase, TLanguage and TTranslateableEntity should be abstract entities. But if so, Associations would not work.

My example is working because I use my own mapping setup, where these classes will never be registered. Only successor classes are registered to my mapping, that's why it is working in my case.

Well, I understand it's not working because you are asking for help, right? :slight_smile:
Such structure is not supported.

It's working fine for me, but I think I don't need to provide a sample project, if you do not support such structure?

regarding this only:

function TMappingAttributeStrategy.LoadColumnsFromManyValuedAssociationMember(RttiInfo: TRttiOptimization): TArray<TColumn>;
...
        if not Clazz.InheritsFrom(MappedMemberRealType.AsInstance.MetaclassType) then
          raise EInvalidMappedByReference.Create(Clazz, RttiInfo.MemberName);
...

I don't know, I got lost in this support topic.

What is the current problem and question? I didn't understand your last comment.