Hi,
When Flushing a single object (Master-Detail), the TCascadeType.RemoveOrphan does not
RemoveOrphan.
thanks
Please provide detailed info: current mapping and code used. Have you also added TCascadeType.Flush to the association?
yes i added TCascadeType.Flush to the Many-Valued Association.
...
fAssociation := aMappingExplorer.GetAssociationByPropertyName(TInvoice, 'InvoiceItemsList');
if fAssociation <> nil then
fAssociation.Cascade := fAssociation.Cascade + [TCascadeType.Flush];
...
Typical case Saving a Invoice(Master) and InvoiceItems(Detail).
More, cascadeFlush simple does not save InvoiceItems when Save Invoice Object with single Object and CascadeFlush , but should?
thanks
Please provide the complete mapping and code you are using to remove the associated orphan and flushing it. You should have CascadeType.SaveUpdate and Flush.
"You should have CascadeType.SaveUpdate and Flush"
CascadeType: [SaveUpdate,Merge,Remove,RemoveOrphan,Flush]
Ok, here is an extract from the code
To generate initial Schema I use Data Modeler. However for options not availabel in Data Modeler,
i add it by code, like:
...
class procedure AddCascadeRemoveOrphan(aMappingExplorer: TMappingExplorer; aClassName: TClass; aPropName: String); //Used for Many-Valued Association
class procedure AddOrderManyValuedAssociation(aMappingExplorer: TMappingExplorer; aMasterClassName: TClass; aMasterPropName: String; aDetailClassName: TClass; aDetailPropNames: String); //uses for Many-Valued Association
class procedure RemoveCascadeFlushOnSingleObject(aMappingExplorer: TMappingExplorer; aClassName: TClass; aPropName: String); //Used for Associations
class procedure AddCascadeFlushOnSingleObject(aMappingExplorer: TMappingExplorer; aClassName: TClass; aPropName: String); //Used for Many-Valued Association
class procedure AddUniqueIndex(...);//In 3.9: New : DBIndex attribute, i will try it next versio ;)
...
class procedure TDBSchemaTuning.AddCascadeRemoveOrphan(aMappingExplorer: TMappingExplorer; aClassName: TClass; aPropName: String);
var
fAssociation: TAssociation;
begin
fAssociation := aMappingExplorer.GetAssociationByPropertyName(aClassName, aPropName);
if fAssociation <> nil then
fAssociation.Cascade := fAssociation.Cascade + [TCascadeType.RemoveOrphan];
end;
...
class procedure TDBSchemaTuning.AddCascadeFlushOnSingleObject( aMappingExplorer: TMappingExplorer; aClassName: TClass; aPropName: String);
var
fAssociation: TAssociation;
begin
fAssociation := aMappingExplorer.GetAssociationByPropertyName(aClassName, aPropName);
if fAssociation <> nil then
fAssociation.Cascade := fAssociation.Cascade + [TCascadeType.Flush];
end;
To add Extra options:
...
AddCascadeRemoveOrphan(TMappingExplorer.Default, TNutPlan, 'FNutPDayList');
AddCascadeRemoveOrphan(TMappingExplorer.Default, TNutPDay, 'FNutMealList');
AddCascadeFlushOnSingleObject( TMappingExplorer.Default, TNutPlan, 'NutPDayList');
AddCascadeFlushOnSingleObject( TMappingExplorer.Default, TNutPDay, 'NutMealList');
...
To Save the Object:
...
Trans := Manager.Connection.BeginTransaction;
aNewDBObject := Manager.Merge(aNewDBObject);
Manager.Flush(aNutPMbr);
Trans.Commit;
...
Applying changes:
when deleting NutMealList item, Flush does not remove orphan.
when update NutMealList item, Flush does not update item in the database //here i will do more tests to confirm
when insert NutMealList item, Flush item was inserted in the database
SCHEMA:
...
[Entity]
[Table('NutPMbr')]
[Description('Nutrition Plan Member')]
[PrimaryJoinColumn('Id')]
TNutPMbr = class(TNutPlan)
private
[Column('NutriotionPlanDate', [TColumnProp.Required])]
[Description('')]
FNutriotionPlanDate: TDateTime;
[Column('StartValidity', [TColumnProp.Required])]
[Description('')]
FStartValidity: TDateTime;
[Column('EndValidity', [TColumnProp.Required])]
[Description('')]
FEndValidity: TDateTime;
[Association([TAssociationProp.Lazy, TAssociationProp.Required], CascadeTypeAll - [TCascadeType.Remove])]
[JoinColumn('MemberId', [TColumnProp.Required], 'Id')]
[Description('')]
FMemberId: Proxy<TMember>;
[Association([TAssociationProp.Lazy, TAssociationProp.Required], CascadeTypeAll - [TCascadeType.Remove])]
[JoinColumn('NutrioId', [TColumnProp.Required], 'Id')]
[Description('')]
FNutrioId: Proxy<TNutrio>;
function GetMemberId: TMember;
procedure SetMemberId(const Value: TMember);
function GetNutrioId: TNutrio;
procedure SetNutrioId(const Value: TNutrio);
public
property NutriotionPlanDate: TDateTime read FNutriotionPlanDate write FNutriotionPlanDate;
property StartValidity: TDateTime read FStartValidity write FStartValidity;
property EndValidity: TDateTime read FEndValidity write FEndValidity;
property MemberId: TMember read GetMemberId write SetMemberId;
property NutrioId: TNutrio read GetNutrioId write SetNutrioId;
end;
[Entity]
[Table('NutPlan')]
[Description('Nutrion Food Plan: Plano Alimentar')]
[Inheritance(TInheritanceStrategy.JoinedTables)]
[Sequence('Id_NutPlan')]
[Id('FId', TIdGenerator.IdentityOrSequence)]
TNutPlan = class
private
[Column('Id', [TColumnProp.Required])]
[Description('')]
FId: Int64;
[Column('Title', [TColumnProp.Required], 30)]
[Description('')]
FTitle: string;
[Column('Comment', [], 120)]
[Description('')]
FComment: Nullable<string>;
[Column('RecVersion', [TColumnProp.Required])]
[Description('')]
FRecVersion: TDateTime;
[ManyValuedAssociation([TAssociationProp.Lazy, TAssociationProp.Required], [TCascadeType.SaveUpdate, TCascadeType.Merge, TCascadeType.Remove], 'FNutPlanId')]
FNutPDayList: Proxy<TList<TNutPDay>>;
function GetNutPDayList: TList<TNutPDay>;
public
constructor Create;
destructor Destroy; override;
property Id: Int64 read FId write FId;
property Title: string read FTitle write FTitle;
property Comment: Nullable<string> read FComment write FComment;
property RecVersion: TDateTime read FRecVersion write FRecVersion;
property NutPDayList: TList<TNutPDay> read GetNutPDayList;
end;
[Entity]
[Table('NutPDay')]
[Description('Nutrition Plan Day')]
[Sequence('Id_NutPDay')]
[Id('FId', TIdGenerator.IdentityOrSequence)]
TNutPDay = class
private
[Column('Id', [TColumnProp.Required])]
[Description('')]
FId: Int64;
[Column('DayWkId', [TColumnProp.Required])]
[Description('Day Of Week (enum TDayOfWeekX): dowAllDays(0), dowSunday(1), dowMonday(2),...')]
FDayWkId: Integer;
[Column('RecVersion', [TColumnProp.Required])]
[Description('')]
FRecVersion: TDateTime;
[Association([TAssociationProp.Lazy, TAssociationProp.Required], CascadeTypeAll - [TCascadeType.Remove])]
[JoinColumn('NutPlanId', [TColumnProp.Required], 'Id')]
[Description('')]
FNutPlanId: Proxy<TNutPlan>;
[ManyValuedAssociation([TAssociationProp.Lazy, TAssociationProp.Required], [TCascadeType.SaveUpdate, TCascadeType.Merge, TCascadeType.Remove], 'FNutPDayId')]
FNutMealList: Proxy<TList<TNutMeal>>;
function GetNutPlanId: TNutPlan;
procedure SetNutPlanId(const Value: TNutPlan);
function GetNutMealList: TList<TNutMeal>;
public
constructor Create;
destructor Destroy; override;
property Id: Int64 read FId write FId;
property DayWkId: Integer read FDayWkId write FDayWkId;
property RecVersion: TDateTime read FRecVersion write FRecVersion;
property NutPlanId: TNutPlan read GetNutPlanId write SetNutPlanId;
property NutMealList: TList<TNutMeal> read GetNutMealList;
end;
[Entity]
[Table('NutMeal')]
[Description('')]
[Sequence('Id_NutMeal')]
[Id('FId', TIdGenerator.IdentityOrSequence)]
TNutMeal = class
private
[Column('Id', [TColumnProp.Required])]
[Description('')]
FId: Int64;
[Column('MealTime', [TColumnProp.Required])]
[Description('MealTime: Hora da Refeição')]
FMealTime: TDateTime;
[Column('FoodDescription', [], 256)]
[Description('')]
FFoodDescription: Nullable<string>;
[Column('RecVersion', [TColumnProp.Required])]
[Description('')]
FRecVersion: TDateTime;
[Association([TAssociationProp.Lazy, TAssociationProp.Required], CascadeTypeAll - [TCascadeType.Remove])]
[JoinColumn('NutPDayId', [TColumnProp.Required], 'Id')]
[Description('')]
FNutPDayId: Proxy<TNutPDay>;
[Association([TAssociationProp.Lazy, TAssociationProp.Required], CascadeTypeAll - [TCascadeType.Remove])]
[JoinColumn('NutMTypId', [TColumnProp.Required], 'Id')]
[Description('')]
FNutMTypId: Proxy<TNutMTyp>;
function GetNutPDayId: TNutPDay;
procedure SetNutPDayId(const Value: TNutPDay);
function GetNutMTypId: TNutMTyp;
procedure SetNutMTypId(const Value: TNutMTyp);
public
property Id: Int64 read FId write FId;
property MealTime: TDateTime read FMealTime write FMealTime;
property FoodDescription: Nullable<string> read FFoodDescription write FFoodDescription;
property RecVersion: TDateTime read FRecVersion write FRecVersion;
property NutPDayId: TNutPDay read GetNutPDayId write SetNutPDayId;
property NutMTypId: TNutMTyp read GetNutMTypId write SetNutMTypId;
end;
...
T H A N K S S S S S S :)
Thank you very much for detailed information. Unfortunately I still cannot spot what could be causing the issue you are getting.
Hi
If i call Manager.Flush(), instead of Manager.Flush(aNutPMbr), it's OK.
The issue is Fushing single Object.
I will try what you suggest
Thanks
sorry, my mistake
Everything works as expected. Wrong parameters when call "AddCascadeFlushOnSingleObject"
and "AddCascadeRemoveOrphan"
To add Extra options(correct version):
...
AddCascadeRemoveOrphan(TMappingExplorer.Default, TNutPMbr, 'FNutPDayList');
AddCascadeRemoveOrphan(TMappingExplorer.Default, TNutPDay, 'FNutMealList');
AddCascadeFlushOnSingleObject( TMappingExplorer.Default, TNutPMbr, 'NutPDayList');
AddCascadeFlushOnSingleObject( TMappingExplorer.Default, TNutPDay, 'NutMealList');
Thanks, sorry again