diff --git a/README.md b/README.md index bc411dc2..83b2a254 100644 --- a/README.md +++ b/README.md @@ -1423,16 +1423,22 @@ The current beta release is named 3.2.3-radium-beta. If you want to stay on the- - mid-air-collision handling now uses SHA1 instead of MD5 -- Added `MVCFramework.Commons.MVC_HTTP_STATUS_CODES` const array containing all the HTTP status codes with its `ReasonString` +- Added `MVCFramework.Commons.MVC_HTTP_STATUS_CODES` const array containing all the HTTP status codes with its `ReasonString`. + +- Support for `TObject` descendants in JSONRPC APIs (not only for JSONObject and JSONArray). - New global configuration variable `MVCSerializeNulls`. When MVCSerializeNulls = True (default) empty nullables and nil are serialized as json null. When MVCSerializeNulls = False empty nullables and nil are not serialized at all. -- Nullable types now have `Equal` support and a better "equality check" strategy. +- Nullable types now have `Equal` method support, the new method `TryHasValue(out Value)` works like `HasValue` but returns the contained value if present. Also there is a better "equality check" strategy. - Unit tests now are always executed for Win32 and Win64 bit (both client and server). +- Added `TMVCActiveRecord.Refresh` method + +- Unit test suites generates one NUnit XML output file for each platform + - New built-in profiler (usable with Delphi 10.4+) - to profile a block of code, write the following ```delphi diff --git a/samples/activerecord_showcase/MainFormU.dfm b/samples/activerecord_showcase/MainFormU.dfm index a8aac854..5d1aeb54 100644 --- a/samples/activerecord_showcase/MainFormU.dfm +++ b/samples/activerecord_showcase/MainFormU.dfm @@ -258,6 +258,15 @@ object MainForm: TMainForm TabOrder = 24 OnClick = btnSpeedClick end + object btnRefresh: TButton + Left = 144 + Top = 442 + Width = 121 + Height = 34 + Caption = 'Manual Refresh' + TabOrder = 25 + OnClick = btnRefreshClick + end object FDConnection1: TFDConnection Left = 312 Top = 40 diff --git a/samples/activerecord_showcase/MainFormU.pas b/samples/activerecord_showcase/MainFormU.pas index f75c19f9..53e1ea89 100644 --- a/samples/activerecord_showcase/MainFormU.pas +++ b/samples/activerecord_showcase/MainFormU.pas @@ -57,6 +57,7 @@ type btnOOP: TButton; btnReadOnly: TButton; btnSpeed: TButton; + btnRefresh: TButton; procedure btnCRUDClick(Sender: TObject); procedure btnInheritanceClick(Sender: TObject); procedure btnMultiThreadingClick(Sender: TObject); @@ -84,6 +85,7 @@ type procedure btnOOPClick(Sender: TObject); procedure btnReadOnlyClick(Sender: TObject); procedure btnSpeedClick(Sender: TObject); + procedure btnRefreshClick(Sender: TObject); private procedure Log(const Value: string); procedure LoadCustomers; @@ -1467,6 +1469,44 @@ begin end; end; +procedure TMainForm.btnRefreshClick(Sender: TObject); +var + lCustomer: TCustomer; + lID: Integer; +begin + Log('** Refresh test'); + lCustomer := TCustomer.Create; + try + Log('Entity ' + TCustomer.ClassName + ' is mapped to table ' + lCustomer.TableName); + lCustomer.CompanyName := 'Google Inc.'; + lCustomer.City := 'Montain View, CA'; + lCustomer.Note := 'Μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος οὐλομένην 😁'; + lCustomer.Insert; + Assert('Montain View, CA' = lCustomer.City); + Assert('Μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος οὐλομένην 😁' = lCustomer.Note); + lCustomer.City := ''; + lCustomer.Note := ''; + Log('Refreshing the customer'); + lCustomer.Refresh; + Assert('Montain View, CA' = lCustomer.City); + Assert('Μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος οὐλομένην 😁' = lCustomer.Note); + lID := lCustomer.ID; + finally + lCustomer.Free; + end; + + lCustomer := TCustomer.Create; + try + Log('Loading customer using Refresh'); + lCustomer.ID := lID; + lCustomer.Refresh; + Assert('Montain View, CA' = lCustomer.City); + Assert('Μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος οὐλομένην 😁' = lCustomer.Note); + finally + lCustomer.Free; + end; +end; + procedure TMainForm.btnValidationClick(Sender: TObject); var lCustomer: TCustomerWithLogic; diff --git a/samples/renders/renders.dproj b/samples/renders/renders.dproj index 64c973ee..a8839b78 100644 --- a/samples/renders/renders.dproj +++ b/samples/renders/renders.dproj @@ -220,12 +220,12 @@ true + true - diff --git a/sources/MVCFramework.ActiveRecord.pas b/sources/MVCFramework.ActiveRecord.pas index a11b361b..e03bb5da 100644 --- a/sources/MVCFramework.ActiveRecord.pas +++ b/sources/MVCFramework.ActiveRecord.pas @@ -195,7 +195,6 @@ type fDefaultRQLFilter: string; fMap: TFieldsMap; fPrimaryKey: TRTTIField; - //fBackendDriver: string; fMapping: TMVCFieldsMapping; fPropsAttributes: TArray; fProps: TArray; @@ -224,25 +223,7 @@ type procedure SetAttributes(const AttrName: string; const Value: TValue); function GetTableName: string; protected -// fPrimaryKeyFieldName: string; -// fPrimaryKeyOptions: TMVCActiveRecordFieldOptions; -// fPrimaryKeySequenceName: string; -// fPrimaryKeyFieldType: TFieldType; -// fEntityAllowedActions: TMVCEntityActions; -// fRTTIType: TRttiInstanceType; -// fObjAttributes: TArray; -// fTableName: string; -// fDefaultRQLFilter: string; -// fMap: TFieldsMap; -// fPrimaryKey: TRTTIField; fBackendDriver: string; -// fMapping: TMVCFieldsMapping; -// fPropsAttributes: TArray; -// fProps: TArray; - -// fPartitionInfoInternal: TPartitionInfo; -// fPartitionClause: String; - fTableMap: TMVCTableMap; function GetPartitionInfo: TPartitionInfo; function GetBackEnd: string; @@ -280,6 +261,7 @@ type class function GetByPK(aActiveRecord: TMVCActiveRecord; const aValue: string; const aFieldType: TFieldType; const RaiseExceptionIfNotFound: Boolean): TMVCActiveRecord; overload; + // load events /// /// Called everywhere before persist object into database @@ -364,6 +346,10 @@ type /// Executes an Insert (pk is null) or an Update (pk is not null) /// procedure Store; + /// + /// Reload the current instance from database if the primary key is not empty. + /// + procedure Refresh; virtual; function CheckAction(const aEntityAction: TMVCEntityAction; const aRaiseException: Boolean = True): Boolean; procedure Insert; @@ -2381,6 +2367,29 @@ begin // do nothing end; +procedure TMVCActiveRecord.Refresh; +begin + if not GetPK.IsEmpty then + begin + case GetPrimaryKeyFieldType of + ftLargeInt: begin + LoadByPK(GetPK.AsInt64); + end; + ftInteger: begin + LoadByPK(GetPK.AsInteger); + end; + ftString: begin + LoadByPK(GetPK.AsString); + end; + ftGuid: begin + LoadByPK(GetPK.AsType); + end; + else + raise EMVCActiveRecord.Create('Unknown primary key type'); + end; + end; +end; + procedure TMVCActiveRecord.RemoveChildren(const ChildObject: TObject); begin if fChildren <> nil then @@ -2885,51 +2894,35 @@ begin begin if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value); end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value) end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value) end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value) end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value) end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value) end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := Value.AsType().Value; + Result := Value.AsType().TryHasValue(Value) end else if Value.IsType() then begin - Result := Value.AsType().HasValue; - if Result then - Value := TValue.From(Value.AsType().Value); + Result := Value.AsType().TryHasValue(Value) end else raise EMVCActiveRecord.Create diff --git a/sources/MVCFramework.Nullables.pas b/sources/MVCFramework.Nullables.pas index 3b91c191..ea5a79cd 100644 --- a/sources/MVCFramework.Nullables.pas +++ b/sources/MVCFramework.Nullables.pas @@ -32,7 +32,7 @@ unit MVCFramework.Nullables; interface uses - System.SysUtils, System.Classes, System.TypInfo; + System.SysUtils, System.Classes, System.TypInfo, System.RTTI; type EMVCNullable = class(Exception) @@ -56,7 +56,7 @@ type class operator Implicit(const Value: String): NullableString; class operator Implicit(const Value: NullableString): String; class operator Implicit(const Value: Pointer): NullableString; - class operator Equal(LeftValue: NullableString; RightValue: NullableString) : Boolean; + class operator Equal(LeftValue: NullableString; RightValue: NullableString) : Boolean; /// ///Returns `True` if the NullableString contains a value /// @@ -82,6 +82,14 @@ type /// function Equals(const Value: NullableString): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: String): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: String read GetValue write SetValue; @@ -104,7 +112,7 @@ type class operator Implicit(const Value: Currency): NullableCurrency; class operator Implicit(const Value: NullableCurrency): Currency; class operator Implicit(const Value: Pointer): NullableCurrency; - class operator Equal(LeftValue: NullableCurrency; RightValue: NullableCurrency) : Boolean; + class operator Equal(LeftValue: NullableCurrency; RightValue: NullableCurrency) : Boolean; /// ///Returns `True` if the NullableCurrency contains a value /// @@ -130,6 +138,14 @@ type /// function Equals(const Value: NullableCurrency): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Currency): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Currency read GetValue write SetValue; @@ -152,7 +168,7 @@ type class operator Implicit(const Value: Boolean): NullableBoolean; class operator Implicit(const Value: NullableBoolean): Boolean; class operator Implicit(const Value: Pointer): NullableBoolean; - class operator Equal(LeftValue: NullableBoolean; RightValue: NullableBoolean) : Boolean; + class operator Equal(LeftValue: NullableBoolean; RightValue: NullableBoolean) : Boolean; /// ///Returns `True` if the NullableBoolean contains a value /// @@ -178,6 +194,14 @@ type /// function Equals(const Value: NullableBoolean): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Boolean): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Boolean read GetValue write SetValue; @@ -200,7 +224,7 @@ type class operator Implicit(const Value: TDate): NullableTDate; class operator Implicit(const Value: NullableTDate): TDate; class operator Implicit(const Value: Pointer): NullableTDate; - class operator Equal(LeftValue: NullableTDate; RightValue: NullableTDate) : Boolean; + class operator Equal(LeftValue: NullableTDate; RightValue: NullableTDate) : Boolean; /// ///Returns `True` if the NullableTDate contains a value /// @@ -226,6 +250,14 @@ type /// function Equals(const Value: NullableTDate): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TDate): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: TDate read GetValue write SetValue; @@ -248,7 +280,7 @@ type class operator Implicit(const Value: TTime): NullableTTime; class operator Implicit(const Value: NullableTTime): TTime; class operator Implicit(const Value: Pointer): NullableTTime; - class operator Equal(LeftValue: NullableTTime; RightValue: NullableTTime) : Boolean; + class operator Equal(LeftValue: NullableTTime; RightValue: NullableTTime) : Boolean; /// ///Returns `True` if the NullableTTime contains a value /// @@ -274,6 +306,14 @@ type /// function Equals(const Value: NullableTTime): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TTime): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: TTime read GetValue write SetValue; @@ -296,7 +336,7 @@ type class operator Implicit(const Value: TDateTime): NullableTDateTime; class operator Implicit(const Value: NullableTDateTime): TDateTime; class operator Implicit(const Value: Pointer): NullableTDateTime; - class operator Equal(LeftValue: NullableTDateTime; RightValue: NullableTDateTime) : Boolean; + class operator Equal(LeftValue: NullableTDateTime; RightValue: NullableTDateTime) : Boolean; /// ///Returns `True` if the NullableTDateTime contains a value /// @@ -322,6 +362,14 @@ type /// function Equals(const Value: NullableTDateTime): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TDateTime): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: TDateTime read GetValue write SetValue; @@ -344,7 +392,7 @@ type class operator Implicit(const Value: Single): NullableSingle; class operator Implicit(const Value: NullableSingle): Single; class operator Implicit(const Value: Pointer): NullableSingle; - class operator Equal(LeftValue: NullableSingle; RightValue: NullableSingle) : Boolean; + class operator Equal(LeftValue: NullableSingle; RightValue: NullableSingle) : Boolean; /// ///Returns `True` if the NullableSingle contains a value /// @@ -370,6 +418,14 @@ type /// function Equals(const Value: NullableSingle): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Single): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Single read GetValue write SetValue; @@ -392,7 +448,7 @@ type class operator Implicit(const Value: Double): NullableDouble; class operator Implicit(const Value: NullableDouble): Double; class operator Implicit(const Value: Pointer): NullableDouble; - class operator Equal(LeftValue: NullableDouble; RightValue: NullableDouble) : Boolean; + class operator Equal(LeftValue: NullableDouble; RightValue: NullableDouble) : Boolean; /// ///Returns `True` if the NullableDouble contains a value /// @@ -418,6 +474,14 @@ type /// function Equals(const Value: NullableDouble): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Double): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Double read GetValue write SetValue; @@ -440,7 +504,7 @@ type class operator Implicit(const Value: Extended): NullableExtended; class operator Implicit(const Value: NullableExtended): Extended; class operator Implicit(const Value: Pointer): NullableExtended; - class operator Equal(LeftValue: NullableExtended; RightValue: NullableExtended) : Boolean; + class operator Equal(LeftValue: NullableExtended; RightValue: NullableExtended) : Boolean; /// ///Returns `True` if the NullableExtended contains a value /// @@ -466,6 +530,14 @@ type /// function Equals(const Value: NullableExtended): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Extended): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Extended read GetValue write SetValue; @@ -488,7 +560,7 @@ type class operator Implicit(const Value: Int16): NullableInt16; class operator Implicit(const Value: NullableInt16): Int16; class operator Implicit(const Value: Pointer): NullableInt16; - class operator Equal(LeftValue: NullableInt16; RightValue: NullableInt16) : Boolean; + class operator Equal(LeftValue: NullableInt16; RightValue: NullableInt16) : Boolean; /// ///Returns `True` if the NullableInt16 contains a value /// @@ -514,6 +586,14 @@ type /// function Equals(const Value: NullableInt16): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Int16): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Int16 read GetValue write SetValue; @@ -536,7 +616,7 @@ type class operator Implicit(const Value: UInt16): NullableUInt16; class operator Implicit(const Value: NullableUInt16): UInt16; class operator Implicit(const Value: Pointer): NullableUInt16; - class operator Equal(LeftValue: NullableUInt16; RightValue: NullableUInt16) : Boolean; + class operator Equal(LeftValue: NullableUInt16; RightValue: NullableUInt16) : Boolean; /// ///Returns `True` if the NullableUInt16 contains a value /// @@ -562,6 +642,14 @@ type /// function Equals(const Value: NullableUInt16): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: UInt16): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: UInt16 read GetValue write SetValue; @@ -584,7 +672,7 @@ type class operator Implicit(const Value: Int32): NullableInt32; class operator Implicit(const Value: NullableInt32): Int32; class operator Implicit(const Value: Pointer): NullableInt32; - class operator Equal(LeftValue: NullableInt32; RightValue: NullableInt32) : Boolean; + class operator Equal(LeftValue: NullableInt32; RightValue: NullableInt32) : Boolean; /// ///Returns `True` if the NullableInt32 contains a value /// @@ -610,6 +698,14 @@ type /// function Equals(const Value: NullableInt32): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Int32): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Int32 read GetValue write SetValue; @@ -632,7 +728,7 @@ type class operator Implicit(const Value: UInt32): NullableUInt32; class operator Implicit(const Value: NullableUInt32): UInt32; class operator Implicit(const Value: Pointer): NullableUInt32; - class operator Equal(LeftValue: NullableUInt32; RightValue: NullableUInt32) : Boolean; + class operator Equal(LeftValue: NullableUInt32; RightValue: NullableUInt32) : Boolean; /// ///Returns `True` if the NullableUInt32 contains a value /// @@ -658,6 +754,14 @@ type /// function Equals(const Value: NullableUInt32): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: UInt32): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: UInt32 read GetValue write SetValue; @@ -680,7 +784,7 @@ type class operator Implicit(const Value: Int64): NullableInt64; class operator Implicit(const Value: NullableInt64): Int64; class operator Implicit(const Value: Pointer): NullableInt64; - class operator Equal(LeftValue: NullableInt64; RightValue: NullableInt64) : Boolean; + class operator Equal(LeftValue: NullableInt64; RightValue: NullableInt64) : Boolean; /// ///Returns `True` if the NullableInt64 contains a value /// @@ -706,6 +810,14 @@ type /// function Equals(const Value: NullableInt64): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: Int64): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: Int64 read GetValue write SetValue; @@ -728,7 +840,7 @@ type class operator Implicit(const Value: UInt64): NullableUInt64; class operator Implicit(const Value: NullableUInt64): UInt64; class operator Implicit(const Value: Pointer): NullableUInt64; - class operator Equal(LeftValue: NullableUInt64; RightValue: NullableUInt64) : Boolean; + class operator Equal(LeftValue: NullableUInt64; RightValue: NullableUInt64) : Boolean; /// ///Returns `True` if the NullableUInt64 contains a value /// @@ -754,6 +866,14 @@ type /// function Equals(const Value: NullableUInt64): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: UInt64): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: UInt64 read GetValue write SetValue; @@ -776,7 +896,7 @@ type class operator Implicit(const Value: TGUID): NullableTGUID; class operator Implicit(const Value: NullableTGUID): TGUID; class operator Implicit(const Value: Pointer): NullableTGUID; - class operator Equal(LeftValue: NullableTGUID; RightValue: NullableTGUID) : Boolean; + class operator Equal(LeftValue: NullableTGUID; RightValue: NullableTGUID) : Boolean; /// ///Returns `True` if the NullableTGUID contains a value /// @@ -802,6 +922,14 @@ type /// function Equals(const Value: NullableTGUID): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TGUID): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: TGUID read GetValue write SetValue; @@ -845,6 +973,25 @@ begin end; end; +function NullableString.TryHasValue(out Value: String): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableString.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableString.Clear; begin SetNull; @@ -935,6 +1082,25 @@ begin end; end; +function NullableCurrency.TryHasValue(out Value: Currency): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableCurrency.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableCurrency.Clear; begin SetNull; @@ -1025,6 +1191,25 @@ begin end; end; +function NullableBoolean.TryHasValue(out Value: Boolean): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableBoolean.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableBoolean.Clear; begin SetNull; @@ -1115,6 +1300,25 @@ begin end; end; +function NullableTDate.TryHasValue(out Value: TDate): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableTDate.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableTDate.Clear; begin SetNull; @@ -1205,6 +1409,25 @@ begin end; end; +function NullableTTime.TryHasValue(out Value: TTime): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableTTime.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableTTime.Clear; begin SetNull; @@ -1295,6 +1518,25 @@ begin end; end; +function NullableTDateTime.TryHasValue(out Value: TDateTime): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableTDateTime.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableTDateTime.Clear; begin SetNull; @@ -1385,6 +1627,25 @@ begin end; end; +function NullableSingle.TryHasValue(out Value: Single): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableSingle.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableSingle.Clear; begin SetNull; @@ -1475,6 +1736,25 @@ begin end; end; +function NullableDouble.TryHasValue(out Value: Double): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableDouble.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableDouble.Clear; begin SetNull; @@ -1565,6 +1845,25 @@ begin end; end; +function NullableExtended.TryHasValue(out Value: Extended): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableExtended.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableExtended.Clear; begin SetNull; @@ -1655,6 +1954,25 @@ begin end; end; +function NullableInt16.TryHasValue(out Value: Int16): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableInt16.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableInt16.Clear; begin SetNull; @@ -1744,6 +2062,25 @@ begin end; end; +function NullableUInt16.TryHasValue(out Value: UInt16): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableUInt16.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableUInt16.Clear; begin SetNull; @@ -1833,6 +2170,25 @@ begin end; end; +function NullableInt32.TryHasValue(out Value: Int32): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableInt32.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableInt32.Clear; begin SetNull; @@ -1922,6 +2278,25 @@ begin end; end; +function NullableUInt32.TryHasValue(out Value: UInt32): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableUInt32.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableUInt32.Clear; begin SetNull; @@ -2011,6 +2386,25 @@ begin end; end; +function NullableInt64.TryHasValue(out Value: Int64): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableInt64.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableInt64.Clear; begin SetNull; @@ -2100,6 +2494,25 @@ begin end; end; +function NullableUInt64.TryHasValue(out Value: UInt64): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableUInt64.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableUInt64.Clear; begin SetNull; @@ -2189,6 +2602,25 @@ begin end; end; +function NullableTGUID.TryHasValue(out Value: TGUID): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function NullableTGUID.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From(fValue); + end; +end; + + procedure NullableTGUID.Clear; begin SetNull; diff --git a/sources/MVCFramework.Nullables.pas.template b/sources/MVCFramework.Nullables.pas.template index 0c0eed4d..ff9efc3e 100644 --- a/sources/MVCFramework.Nullables.pas.template +++ b/sources/MVCFramework.Nullables.pas.template @@ -32,7 +32,7 @@ unit MVCFramework.Nullables; interface uses - System.SysUtils, System.Classes, System.TypInfo; + System.SysUtils, System.Classes, System.TypInfo, System.RTTI; type EMVCNullable = class(Exception) @@ -53,7 +53,7 @@ type class operator Implicit(const Value: $TYPE$): Nullable$TYPE$; class operator Implicit(const Value: Nullable$TYPE$): $TYPE$; class operator Implicit(const Value: Pointer): Nullable$TYPE$; - class operator Equal(LeftValue: Nullable$TYPE$; RightValue: Nullable$TYPE$) : Boolean; + class operator Equal(LeftValue: Nullable$TYPE$; RightValue: Nullable$TYPE$) : Boolean; /// ///Returns `True` if the Nullable$TYPE$ contains a value /// @@ -79,6 +79,14 @@ type /// function Equals(const Value: Nullable$TYPE$): Boolean; /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: $TYPE$): Boolean; overload; + /// + ///Returns true if the nullable contains a value and returns the contained value in the out Value parameter. + /// + function TryHasValue(out Value: TValue): Boolean; overload; + /// ///Returns the value stored or raises exception if no value is stored /// property Value: $TYPE$ read GetValue write SetValue; @@ -103,6 +111,25 @@ begin end; end; +function Nullable$TYPE$.TryHasValue(out Value: $TYPE$): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := fValue; + end; +end; + +function Nullable$TYPE$.TryHasValue(out Value: TValue): Boolean; +begin + Result := HasValue; + if Result then + begin + Value := TValue.From<$TYPE$>(fValue); + end; +end; + + procedure Nullable$TYPE$.Clear; begin SetNull; diff --git a/unittests/general/Several/ActiveRecordTestsU.pas b/unittests/general/Several/ActiveRecordTestsU.pas index f3726784..d72553e7 100644 --- a/unittests/general/Several/ActiveRecordTestsU.pas +++ b/unittests/general/Several/ActiveRecordTestsU.pas @@ -52,6 +52,8 @@ type [Test] procedure TestCRUD; [Test] + procedure TestRefresh; + [Test] procedure Test_ISSUE485; [Test] procedure TestDeleteIfNotFound; @@ -1488,8 +1490,7 @@ begin CreateACustomer('New York 1', 'New York', 5); CreateACustomer('Toyko 1', 'Tokyo', 4); - var - lRomeCustomer := TMVCActiveRecord.SelectOneByRQL('contains(CompanyName,"1")'); + var lRomeCustomer := TMVCActiveRecord.SelectOneByRQL('contains(CompanyName,"1")'); try Assert.IsNotNull(lRomeCustomer); finally @@ -1513,6 +1514,48 @@ begin end; end; +procedure TTestActiveRecordBase.TestRefresh; +var + lCustomer: TCustomer; + lID: Integer; +begin + Assert.AreEqual(Int64(0), TMVCActiveRecord.Count()); + lCustomer := TCustomer.Create; + try + lCustomer.CompanyName := 'bit Time Professionals'; + lCustomer.City := 'Rome, IT'; + lCustomer.Note := 'note1'; + lCustomer.CreationTime := Time; + lCustomer.CreationDate := Date; + lCustomer.ID := -1; { don't be fooled by the default! } + lCustomer.Insert; + lID := lCustomer.ID; + Assert.AreEqual(1, lID); + lCustomer.CompanyName.Clear; + lCustomer.City := ''; + lCustomer.Note := ''; + lCustomer.CreationTime := 0; + lCustomer.CreationDate := 0; + lCustomer.Refresh; + Assert.AreEqual('bit Time Professionals', lCustomer.CompanyName.ValueOrDefault); + Assert.AreEqual('Rome, IT', lCustomer.City); + Assert.AreEqual('note1', lCustomer.Note); + finally + lCustomer.Free; + end; + + lCustomer := TCustomer.Create; + try + lCustomer.ID := lID; + lCustomer.Refresh; + Assert.AreEqual('bit Time Professionals', lCustomer.CompanyName.ValueOrDefault); + Assert.AreEqual('Rome, IT', lCustomer.City); + Assert.AreEqual('note1', lCustomer.Note); + finally + lCustomer.Free; + end; +end; + procedure TTestActiveRecordBase.TestRQL; var lCustomers: TObjectList; diff --git a/unittests/general/Several/DMVCFrameworkTests.dpr b/unittests/general/Several/DMVCFrameworkTests.dpr index 64cdc3a2..1701ad2d 100644 --- a/unittests/general/Several/DMVCFrameworkTests.dpr +++ b/unittests/general/Several/DMVCFrameworkTests.dpr @@ -8,9 +8,11 @@ program DMVCFrameworkTests; uses System.SysUtils, + System.IOUtils, DUnitX.TestFramework, {$IFDEF CONSOLE_TESTRUNNER} DUnitX.Loggers.Console, + DUnitX.Loggers.XML.NUnit, {$ENDIF } {$IFDEF TESTINSIGHT} TestInsight.DUnitX, @@ -83,6 +85,7 @@ var runner: ITestRunner; results: IRunResults; logger: ITestLogger; + OutputNUnitFolder: String; begin try // Check command line options, will exit if invalid @@ -95,9 +98,25 @@ begin // Log to the console window logger := TDUnitXConsoleLogger.Create(True); runner.AddLogger(logger); + // Generate an NUnit compatible XML File - // nunitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile); - // runner.AddLogger(nunitLogger); + if TDUnitX.Options.XMLOutputFile.IsEmpty then + begin + OutputNUnitFolder := TPath.Combine( + TDirectory.GetParent(TDirectory.GetParent(TDirectory.GetParent(AppPath))), 'UnitTestReports'); + TDirectory.CreateDirectory(OutputNUnitFolder); + {$if defined(win32)} + TDUnitX.Options.XMLOutputFile := TPath.Combine(OutputNUnitFolder,'dmvcframework_nunit_win32.xml'); + {$endif} + {$if defined(win64)} + TDUnitX.Options.XMLOutputFile := TPath.Combine(OutputNUnitFolder, 'dmvcframework_nunit_win64.xml'); + {$endif} + {$if defined(linux64)} + TDUnitX.Options.XMLOutputFile := TPath.Combine(OutputNUnitFolder, 'dmvcframework_nunit_linux64.xml'); + {$endif} + end; + + runner.AddLogger(TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile)); runner.FailsOnNoAsserts := False; // When true, Assertions must be made during tests; // Run tests diff --git a/unittests/general/Several/DMVCFrameworkTests.dproj b/unittests/general/Several/DMVCFrameworkTests.dproj index cc53845e..a9d0b297 100644 --- a/unittests/general/Several/DMVCFrameworkTests.dproj +++ b/unittests/general/Several/DMVCFrameworkTests.dproj @@ -236,7 +236,6 @@
TestWebModule2
- dfm TWebModule
diff --git a/unittests/general/UnitTestReports/dmvcframework_nunit_win32.xml b/unittests/general/UnitTestReports/dmvcframework_nunit_win32.xml new file mode 100644 index 00000000..4f249820 --- /dev/null +++ b/unittests/general/UnitTestReports/dmvcframework_nunit_win32.xml @@ -0,0 +1,1001 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unittests/general/UnitTestReports/dmvcframework_nunit_win64.xml b/unittests/general/UnitTestReports/dmvcframework_nunit_win64.xml new file mode 100644 index 00000000..77a6c6ac --- /dev/null +++ b/unittests/general/UnitTestReports/dmvcframework_nunit_win64.xml @@ -0,0 +1,997 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +