Base solution for your next web application

Activities of "tom.ohle"

On the server side, I would like to send emails to users based on their CultureInfo setting stored.

I cannot use CultureInfo.CurrentCulture because I don't want the CultureInfo information for the current logged in user. I want it for another user.

It appears that if a user chooses a language that is different from default the entry will display in the AbpSettings table. However, what would be the best way programmatically to retrieve this data in the Asp.Net Core project?

Hi experts, We have tight security on our servers and we recently migrated from hosting our Asp.Net Zero application in Wndows to Linux. After doing so, we found that calls to the SendEmailAsync in the Abp MailKitEmailSender class would just hang and never complete. After doing some extensive network sniffing, we found that the following call was being made out to the internet and was blocked because of our security: ocsp.digicert.com/MFEwTzBNMEswSTAJBgUrDgMCGgUABBQQX6Z6gAidtSefNc6DC0OInqPHDQQUD4BhHIIxYdUvKOeNRji0LOHG2eICEAkNK6BEikcNL2V2lRiqNKM= Port: 72.21.91.29

Once we opened we opened this port up the SendEmailAsync method worked with no issue. However, our network team will not open this port up for us on our servers until we can explain the purpose of this call. I'm not sure why MailKit would check/download a certificate since we are not using SSL for our emails and have the Use SSL checkbox unchecked.

Would you be able to help?

Please if you do not mind I'd appreciate it if you could delete 'WIP_Write_Off' and leave 'Stantec.WIP_Write_Off'

That is really good to know how the naming system works against the project count.

Thanks,

Tom.

Hello, we accidentally created the new WIP_Write_Off project when we really meant to just download a specific version (v6.9.1) for an existing project Stantec.WIP_Write_Off. Is there anyway you can help us delete that WIP_Write_Off project, but still keep the existing Stantec.WIP_Write_Off project?

UPDATE It looks like our project count is still accurate which is what I was mainly concerned about. Therefore, no action needed on your part - I'm OK if this project is not deleted.

Per the documentation (https://github.com/abpframework/abp/blob/master/docs/en/Background-Jobs.md#configuration) I should be able to change the default timeout by overriding the ConfigureServices method but the AbpBackgroundJobsModule cannot be resolved.

Is there another way to go about this? Can the timeout be changed for individual background jobs?

The documentation also references the Volo.Abp.BackgroundJobs nuget package but none of the projects within the ASP.NET Zero generated project reference the nuget package.

Thanks!

We ended up adding the below css to src/angular/src/styles.css to fix this issue.

.swal2-popup.swal2-toast {
    padding: 12px 16px 12px 0px !important;
}

The notification is called like this in tenant-settings.component.ts.

this.notify.info(this.l('SavedSuccessfully'));

And this is what it looks like in the browser (Google Chrome, Version 74.0.3729.108 (Official Build) (64-bit)).

Thanks for the tip, but it didn't work. Here is the error I get in the logs after making the suggested change.

No property or field 'Id' exists in type 'GetDocumentForView' (at index 0)

I was able to solve this with the following workaround. It looks like LINQ doesn't like the ObjectMapper.Map usage. Here is the original GetAll method for Documents.

public async Task<PagedResultDto<GetDocumentForView>> GetAll(GetAllDocumentsInput input)
{
  var filteredDocuments = _documentRepository.GetAll()
        .WhereIf(!string.IsNullOrWhiteSpace(input.Filter), e => false)
        .WhereIf(input.MinStatusFilter != null, e => e.Status >= input.MinStatusFilter)
        .WhereIf(input.MaxStatusFilter != null, e => e.Status <= input.MaxStatusFilter);
  
  var query = from o in filteredDocuments
              select new GetDocumentForView()
              {
                Document = ObjectMapper.Map<DocumentDto>(o)
              };

  var totalCount = await query.CountAsync();

  var documents = await query
    .OrderBy(input.Sorting ?? "document.id asc")
    .PageBy(input)
    .ToListAsync();

  return new PagedResultDto<GetDocumentForView>(
      totalCount,
      documents
  );
}

Once I replaced the ObjectMapper code with a DTO construction specific to the Document entity, the LINQ to SQL translation went through. So instead of doing this...

var query = from o in filteredDocuments
            select new GetDocumentForView()
            {
              Document = ObjectMapper.Map<DocumentDto>(o)
            };

...I did this...

var query = from o in filteredDocuments
            select new GetDocumentForView()
            {
              Document = new DocumentDto()
              {
                Id = o.Id,
                Status = o.Status
              }
            };

This seems to work just fine, but it does require more code.

Is there a better way to do this that I am not seeing?

I used the RAD tools to generate an entity, then migrated over 3 million records into the generated table. Now the app hangs attempting to retrieve a 10 record page of results. I added a logger to the DBContext and found that it is waiting for a long running query.

SELECT [e].*
FROM [MyEntityTable] AS [e]
LEFT JOIN (
    SELECT [e0].*
    FROM [MyOtherEntityTable] AS [e0]
    WHERE ([e0].[IsDeleted] = 0) OR ([e0].[IsDeleted] <> 'True')
) AS [t] ON [e].[DocumentId] = [t].[Id]
WHERE ([e].[IsDeleted] = 0) OR ([e].[IsDeleted] <> 'True')

The raw SQL query looks weird since I am only going after a page of data (ie. not the entire table). Other captured queries are including pagination in the query, like this one for AbpUserAccounts which includes ORDER BY and OFFSET clauses.

SELECT [e].[UserId] AS [Id], [e].[TenantId], CASE
    WHEN [t].[Id] IS NULL
    THEN N'.' ELSE [t].[TenancyName]
END AS [TenancyName], [e].[UserName], [e].[LastLoginTime]
FROM [AbpUserAccounts] AS [e]
LEFT JOIN (
    SELECT [e0].*
    FROM [AbpTenants] AS [e0]
    WHERE ([e0].[IsDeleted] = 0) OR ([e0].[IsDeleted] <> @__ef_filter__IsSoftDeleteFilterEnabled_1)
) AS [t] ON [e].[TenantId] = [t].[Id]
WHERE (([e].[IsDeleted] = 0) OR ([e].[IsDeleted] <> @__ef_filter__IsSoftDeleteFilterEnabled_0)) AND ((((([e].[TenantId] <> @__currentUserIdentifier_TenantId_0) OR [e].[TenantId] IS NULL) OR ([e].[UserId] <> @__currentUserIdentifier_UserId_1)) AND [e].[UserLinkId] IS NOT NULL) AND [e].[UserLinkId] IS NULL)
ORDER BY [e].[UserName]
OFFSET @__p_3 ROWS FETCH NEXT @__p_4 ROWS ONLY

I noticed these warnings which could be the culprit.

Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'orderby __ObjectMapper_0.Map([e]).Status desc' could not be translated and will be evaluated locally.
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'Skip(__p_1)' could not be translated and will be evaluated locally.
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'Take(__p_2)' could not be translated and will be evaluated locally.

It looks like the query is running without pagination against the database, and then applied once the data is loaded into application memory. This agrees with my observation of the app's memory usage while waiting for this query to return.

I found this generated code for my entity which I believe should take care of pagination, but instead seems to generate the warnings above.

var myEntities = await query
  .OrderBy(input.Sorting ?? "myEntity.id asc")
  .PageBy(input)
  .ToListAsync();

Any idea why these LINQ expressions could not be translated?

I figured it out. Finally.

I wrongly assumed that the OpenID Connect configuration document would be retrieved from...

https://login.microsoftonline.com/<tenantid>/.well-known/openid-configuration

...when in fact it was coming from...

https://sts.windows.net/<tenantid>/.well-known/openid-configuration

...instead.

A quick curl from within the running host container revealed that a self-signed certificate was found in the certificate chain. This makes sense because our corporate network environment does some SSL interception. The docker container doesn't have this certificate by default, but my docker host OS (Windows 10) does. I browsed to the config url with Chrome and exported the root certificate from the certificate path as a 'Base-64 encoded X.509 (.CER)' file. I set the extension to 'crt', and edited the Host Dockerfile like so...

FROM microsoft/dotnet:2.1-aspnetcore-runtime
COPY RootCA.crt /usr/local/share/ca-certificates
RUN update-ca-certificates
...

Now the certificate is added to the container and the document can be retrieved.

Showing 31 to 40 of 50 entries