Base solution for your next web application

Activities of "BobIngham"

Answer

Hi @ismcagdas, It's not a custom repository, it's simply declared as a Zero repository at the top of my app service. How do I switch off lazy loading in Zero?

Question

.Net Core, Angular, 11.0.0

Am I missing something here, I was certain EF lazy loading was disabled in Zero. I have a simple call to a table in my application layer:

var listTest = _ngFormSubmissionRepository.GetAll().ToList();

My FormSubmission table has millions of rows and a few parent tables, enabled in code as follows:

    public class NgFormSubmission : FullAuditedEntity<long>, IMustHaveTenant
    {
        public int TenantId { get; set; }
        public long? OrganizationUnitId { get; set; }

        [Required]
        public long UserId { get; set; }

        [ForeignKey("NgFormId")]
        public virtual NgForm NgForm { get; set; }
        public virtual int? NgFormId { get; set; }

        [ForeignKey("NgEntityId")]
        public virtual NgEntity NgEntity { get; set; }
        public System.Guid? NgEntityId { get; set; }

        [ForeignKey("NgMobileDeviceId")]
        public virtual NgMobileDevice NgMobileDevice { get; set; }
        public int? NgMobileDeviceId { get; set; }

        [ForeignKey("NgActionId")]
        public virtual NgAction NgAction { get; set; }
        public long? NgActionId { get; set; }
... code removed for brevity

I make a single call to get a list of FormSubmissions:

var listTest = _ngFormSubmissionRepository.GetAll().ToList();

and the SQL created as a result includes calls to parent entities (NgForm, NgEntity, NgMobileDevice and so on) which is not so bad but also included are calls to children of those entities.

SELECT [t].[ClientCreatedDatetime], [t].[CreationTime], COALESCE([t].[CreatorUserId], CAST(0 AS bigint)), [t].[DeleterUserId], [t].[DeletionTime], [t].[Id], [t].[IsDeleted], [t].[IsFlagged], [t].[LastModificationTime], [t].[LastModifierUserId], CASE
    WHEN [t0].[Id] IS NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [t0].[CreationTime], COALESCE([t0].[CreatorUserId], CAST(0 AS bigint)), [t0].[DeleterUserId], [t0].[DeletionTime], [t0].[DisplayName], [t0].[ExtensionData], [t0].[Id], [t0].[IsDeleted], [t0].[LastModificationTime], [t0].[LastModifierUserId], CASE
    WHEN [t1].[Id] IS NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [t1].[CreationTime], COALESCE([t1].[CreatorUserId], CAST(0 AS bigint)), [t1].[DeleterUserId], [t1].[DeletionTime], [t1].[Description], [t1].[DisplayName], [t1].[ExtensionData], [t1].[FormReportTemplate], [t1].[Id], [t1].[IsDeleted], [t1].[IsEditable], [t1].[LastModificationTime], [t1].[LastModifierUserId], [t1].[SerializedJson], [t1].[TenantId], [t0].[NgFormId], [t0].[OrganizationUnitId], [t0].[ParentId], [t0].[TenantId], [t].[NgActionId], CASE
    WHEN [t2].[Id] IS NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [t2].[CreationTime], COALESCE([t2].[CreatorUserId], CAST(0 AS bigint)), [t2].[DeleterUserId], [t2].[DeletionTime], [t2].[DisplayName], [t2].[ExtensionData], [t2].[Id], [t2].[IsDeleted], [t2].[LastModificationTime], [t2].[LastModifierUserId], [t2].[TenantId], [t].[NgEntityId], CASE
    WHEN [t3].[Id] IS NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [t3].[CreationTime], COALESCE([t3].[CreatorUserId], CAST(0 AS bigint)), [t3].[DeleterUserId], [t3].[DeletionTime], [t3].[Description], [t3].[DisplayName], [t3].[ExtensionData], [t3].[FormReportTemplate], [t3].[Id], [t3].[IsDeleted], [t3].[IsEditable], [t3].[LastModificationTime], [t3].[LastModifierUserId], [t3].[SerializedJson], [t3].[TenantId], [t].[NgFormId], CASE
    WHEN [t4].[Id] IS NULL THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, [t4].[AppVersion], [t4].[AuthorisedTime], [t4].[Cordova], [t4].[CreationTime], COALESCE([t4].[CreatorUserId], CAST(0 AS bigint)), [t4].[DeleterUserId], [t4].[DeletionTime], [t4].[ExtensionData], [t4].[Id], [t4].[IMEI], [t4].[IsAuthorised], [t4].[IsDeleted], [t4].[IsOnline], [t4].[IsVirtual], [t4].[LastModificationTime], [t4].[LastModifierUserId], [t4].[Manufacturer], [t4].[Model], [t5].[Id], [t5].[LoginDatetime], [t5].[LogoutDatetime], [t5].[NgMobileDeviceId], [t5].[TenantId], [t5].[UserId], [t6].[CreationTime], [t6].[CreatorUserId], [t6].[DeleterUserId], [t6].[DeletionTime], [t6].[Directive], [t6].[ExtensionData], [t6].[Id], [t6].[IsDeleted], [t6].[NgMobileDeviceId], [t6].[Priority], [t6].[TenantId], [t4].[Platform], [t4].[Serial], [t4].[TenantId], [t4].[UUID], [t4].[Version], [t].[NgMobileDeviceId], [t].[OrganizationUnitId], [t].[ReportString], [t].[SerializedJson], [t].[TenantId], [t].[UserId]
FROM (
    SELECT [n].[Id], [n].[ClientCreatedDatetime], [n].[CreationTime], [n].[CreatorUserId], [n].[DeleterUserId], [n].[DeletionTime], [n].[IsDeleted], [n].[IsFlagged], [n].[LastModificationTime], [n].[LastModifierUserId], [n].[NgActionId], [n].[NgEntityId], [n].[NgFormId], [n].[NgMobileDeviceId], [n].[OrganizationUnitId], [n].[ReportString], [n].[SerializedJson], [n].[TenantId], [n].[UserId]
    FROM [NgFormSubmission] AS [n]
    WHERE ((((@__ef_filter__p_0 = CAST(1 AS bit)) OR ([n].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_1 = CAST(1 AS bit)) OR ([n].[TenantId] = @__ef_filter__CurrentTenantId_2))) AND ([n].[IsDeleted] = @__isDeleted_0)) AND ([n].[NgEntityId] = @__p_1)
    ORDER BY [n].[ClientCreatedDatetime] DESC
    OFFSET @__p_2 ROWS FETCH NEXT @__p_3 ROWS ONLY
) AS [t]
LEFT JOIN (
    SELECT [n0].[Id], [n0].[CreationTime], [n0].[CreatorUserId], [n0].[DeleterUserId], [n0].[DeletionTime], [n0].[DisplayName], [n0].[ExtensionData], [n0].[IsDeleted], [n0].[LastModificationTime], [n0].[LastModifierUserId], [n0].[NgFormId], [n0].[OrganizationUnitId], [n0].[ParentId], [n0].[TenantId]
    FROM [NgAction] AS [n0]
    WHERE ((@__ef_filter__p_3 = CAST(1 AS bit)) OR ([n0].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_4 = CAST(1 AS bit)) OR ([n0].[TenantId] = @__ef_filter__CurrentTenantId_5))
) AS [t0] ON [t].[NgActionId] = [t0].[Id]
LEFT JOIN (
    SELECT [n1].[Id], [n1].[CreationTime], [n1].[CreatorUserId], [n1].[DeleterUserId], [n1].[DeletionTime], [n1].[Description], [n1].[DisplayName], [n1].[ExtensionData], [n1].[FormReportTemplate], [n1].[IsDeleted], [n1].[IsEditable], [n1].[LastModificationTime], [n1].[LastModifierUserId], [n1].[SerializedJson], [n1].[TenantId]
    FROM [NgForm] AS [n1]
    WHERE ((@__ef_filter__p_6 = CAST(1 AS bit)) OR ([n1].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_7 = CAST(1 AS bit)) OR ([n1].[TenantId] = @__ef_filter__CurrentTenantId_8))
) AS [t1] ON [t0].[NgFormId] = [t1].[Id]
LEFT JOIN (
    SELECT [n2].[Id], [n2].[CreationTime], [n2].[CreatorUserId], [n2].[DeleterUserId], [n2].[DeletionTime], [n2].[DisplayName], [n2].[ExtensionData], [n2].[IsDeleted], [n2].[LastModificationTime], [n2].[LastModifierUserId], [n2].[TenantId]
    FROM [NgEntity] AS [n2]
    WHERE ((@__ef_filter__p_9 = CAST(1 AS bit)) OR ([n2].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_10 = CAST(1 AS bit)) OR ([n2].[TenantId] = @__ef_filter__CurrentTenantId_11))
) AS [t2] ON [t].[NgEntityId] = [t2].[Id]
LEFT JOIN (
    SELECT [n3].[Id], [n3].[CreationTime], [n3].[CreatorUserId], [n3].[DeleterUserId], [n3].[DeletionTime], [n3].[Description], [n3].[DisplayName], [n3].[ExtensionData], [n3].[FormReportTemplate], [n3].[IsDeleted], [n3].[IsEditable], [n3].[LastModificationTime], [n3].[LastModifierUserId], [n3].[SerializedJson], [n3].[TenantId]
    FROM [NgForm] AS [n3]
    WHERE ((@__ef_filter__p_6 = CAST(1 AS bit)) OR ([n3].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_7 = CAST(1 AS bit)) OR ([n3].[TenantId] = @__ef_filter__CurrentTenantId_8))
) AS [t3] ON [t].[NgFormId] = [t3].[Id]
LEFT JOIN (
    SELECT [n4].[Id], [n4].[AppVersion], [n4].[AuthorisedTime], [n4].[Cordova], [n4].[CreationTime], [n4].[CreatorUserId], [n4].[DeleterUserId], [n4].[DeletionTime], [n4].[ExtensionData], [n4].[IMEI], [n4].[IsAuthorised], [n4].[IsDeleted], [n4].[IsOnline], [n4].[IsVirtual], [n4].[LastModificationTime], [n4].[LastModifierUserId], [n4].[Manufacturer], [n4].[Model], [n4].[Platform], [n4].[Serial], [n4].[TenantId], [n4].[UUID], [n4].[Version]
    FROM [NgMobileDevice] AS [n4]
    WHERE ((@__ef_filter__p_12 = CAST(1 AS bit)) OR ([n4].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_13 = CAST(1 AS bit)) OR ([n4].[TenantId] = @__ef_filter__CurrentTenantId_14))
) AS [t4] ON [t].[NgMobileDeviceId] = [t4].[Id]


NOTE THESE LEFT JOINS.....

LEFT JOIN (
    SELECT [n5].[Id], [n5].[LoginDatetime], [n5].[LogoutDatetime], [n5].[NgMobileDeviceId], [n5].[TenantId], [n5].[UserId]
    FROM [NgMobileDeviceAndUserLoginAudit] AS [n5]
    WHERE (@__ef_filter__p_15 = CAST(1 AS bit)) OR ([n5].[TenantId] = @__ef_filter__CurrentTenantId_16)
) AS [t5] ON [t4].[Id] = [t5].[NgMobileDeviceId]
LEFT JOIN (
    SELECT [n6].[CreationTime], [n6].[CreatorUserId], [n6].[DeleterUserId], [n6].[DeletionTime], [n6].[Directive], [n6].[ExtensionData], [n6].[Id], [n6].[IsDeleted], [n6].[NgMobileDeviceId], [n6].[Priority], [n6].[TenantId]
    FROM [NgMobileDeviceOfflineDirective] AS [n6]
    WHERE ((@__ef_filter__p_17 = CAST(1 AS bit)) OR ([n6].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_18 = CAST(1 AS bit)) OR ([n6].[TenantId] = @__ef_filter__CurrentTenantId_19))
) AS [t6] ON [t4].[Id] = [t6].[NgMobileDeviceId]**
ORDER BY [t].[ClientCreatedDatetime] DESC, [t].[Id], [t0].[Id], [t1].[Id], [t2].[Id], [t3].[Id], [t4].[Id], [t5].[Id]

My NgMobileDevice entity has audit tables for geolocation tracking and user sign-in/sign-out, each of which has several million rows making the query very ugly indeed. I thought Zero worked the lines of:

var listTest = _ngFormSubmissionRepository.GetAll()
                .Include(m => m.NgMobileDevice)
                .ToList();

when you wanted to include parents and children? Am I missing something and is there any way I can switch off EF lazy loading? More for my understanding of the application, maybe I've beign doing things incorrectly all these years?????

Cheers, Bob

Hi omkarchoudhari,

I had a similar problem with hundreds of devices using Ionic 5 (cross platform). Try catching the errors in the app code and log them to Sentry. I was using a third party to code the Ionic app and I was able to monitor progress throughout development by analysing errors. A further option is to pull commonly used API call code into a Helper class in the Core module and implement a seperate AppService class for device API calls. You are then easily able to filter any device call errors in the Audit Logs.

Hope that helps. Bob

Perfect, @ismcagdas. Thanks as always!

Hi @ismacagdas,

Thanks for the reply. My problem seems to be with the recommended implementation from Formly, as shown in the image below.

  1. The validation message is invoked within the @NgModule decorator but;
  2. the function is outside of the module itself (3.)

I could use abp.localization.localize javascript function to localize text but I have no idea how I would inject the variable to get "Input {input-label} is required". Can you shine any light on this? Any help would be most appreciated.

.net core, angular, 11.0.0

I am using Formly in my application and to support error validation messages in bootstrap the recommendation is to implement the following in my module class:

imports: [
... code removed for brevity
    FormlyBootstrapModule,
    FormlyModule.forRoot({
      validationMessages: [
        { name: 'required', message: requiredValidationMessage },
        { name: 'minlength', message: lengthValidationMessage },
        { name: 'maxlength', message: lengthValidationMessage },
        { name: 'min', message: minValidationMessage },
        { name: 'max', message: maxValidationMessage },
      ],
    }),

We then have the following (example) function above the @NgModule declaration:

export function requiredValidationMessage(err, field) {
  return `Input ${field.templateOptions.label} is required`;
}

See Formly - built in validations for suggested implementation.

What I would like to achieve is to use pre-existing validation messages from the language xml file but I can find no way to access the language service in my module. Ideally something like this....

export function requiredValidationMessage(err, field) {
  return this.l('RequiredField', field.templateOptions.label);
}

Is there any way to access the language service from within a module class or is there a different way to implement this? Thanks in advance....

Hi @ismcagdas,

Those three lines worked for initial formatting when selected from the datepicker: However, when I go to edit the same I am getting "invalid date":

I have noticed that in edit-tenant-modal.ts that the subscription date is formatted at line 72:

    onShown(): void {
        document.getElementById('Name').focus();

        if (this.tenant.subscriptionEndDateUtc) {
            (this.subscriptionEndDateUtc.nativeElement as any).value = this._dateTimeService.formatDate(
                this.tenant.subscriptionEndDateUtc,
                'F'
            );
        }
    }

Is it the case that I need to format every date passed into my create-or-edit component before I display? I thought this was carried automatically by service proxies os some such other service?

Hi @ismcagdas,

This post taken from the other issue raised by @najeebalmajeedy (Use datetimePicker in angular)

I have the following code in my own module which imports the AppCommon module;

The AppCommon module has the the date picker module declared as a provider:

Yet, when I attempt to use the date picker in my reactive form using the following code:

The first thing I notice is that the date picker displays using US format once a date is selected The subject of my previous post, see above).

I can save this date to the database correctly but when I next come to edit I have an "invalid date" message displayed in the input field:

The component works perfectly when I use the the demo-ui component. What am I missing?

Thanks, I'll close this for now.

Hi @ismcagdas, See Use datetimePicker in angular. I thought it best to handle this all in one place.

Showing 1 to 10 of 619 entries