Base solution for your next web application

Activities of "BigCDogCrew"

AspNetZero v8.4, Abp 5.5, Mvc/jQuery

Inside [account-layout-libs.min.js] and also [account-layout-libs.min.js]

        defaultError404: {
          message: 'Resource not found!',
          details: 'The resource requested could not found on the server.'
        },

The details value should be changed to 'The resource requested could not be found on the server.'

Yes, same database. It's all the same project. The user's should not experience any differences or disruption in workflow.

It will hopefully be presented to the users as the same website (one project, same appsettings.json). I'm thinking the easiest solution might be to install the new Angular project and then retrofit it to accomodate our domain-specific legacy ANZ-Mvc/jQuery pages.

AspNetZero v8.4, Abp 5.5, Mvc/jQuery. (EFCore3)

We are planning to upgrade our project to the latest version of AspNetZero (and EFCore6). We are currently using Mvc/jQuery but want to move in the direction of Angular.

We would like to maintain the legacy Mvc/jQuery pages as we author new pages of the system in Angular. We will update the legacy pages to Angular as time permits...but may take several years.

Is this migration path possible? What challenges will we face?

Thanks for the link. I have read that document a few times and it's starting to make sense, but there is still a missing piece of the puzzle in my mind.  How does the user decide which notification method will apply?  Consider the image attached below.

If I am understanding correctly, all implementations of IRealTimeNotifier will be invoked by each notification. So, if we implement EmailRealtimeNotifier and SMSRealtimeNotifier and (signalR) WebRealTimeNotifier and PushRealTimeNotifier, then all of those classes will receive SendNotificationAysnc invocations for each and every notification created.   Correct?

So, we would then have to include logic in each one of those notifiers to look up the user's notification subscriptions and check to see if that method of notification is appropriate or not.  Correct?  This would result in querying the database four times for each notification to retrieve the user's subscription choices.

Would it not be best to have the publisher/distributor perform this configuration query once and then only invoke the specific IRealTimeNotifier(s) that are desired by the user for that specific type of notification? Or, alternatively...pass the subscription information to the IRealTimeNotifier as an input parameter so the notifier can use it without looking it up.

At the root of this problem is the fact that not all notifications are created equal.  Some are informational and some are critical.  Some occur seldom and others occur frequently.   Users have to decide which method of notification is best for each specific type of notification.

In this example below, my user is not that much concerned about new user registration.  They want to see those notifications when they login to the website, but they don't want to be bothered by email or sms for new users.   However, when a new product is created, they want to be informed immediately by SMS and also receive an email with the details that they can easily archive for future reference.   They are not too concerned about new product settings, but want to receive an email so they can archive those settings for future reference.

BTW... I am NOT advocating radio buttons.  This image below is just a visual aide to help explain the problem.  The actual UI for selecting methods would need to allow for a substantially large number of options.

Has there been any change in this area over the last two years?

This would be a very nice feature to have. We need certain notifications to be delivered by email and other to be delivered by SMS; but ultimately, the user should decide for themselves which or both methods they want to use for any given subscription.

Login As Tenant -VS- Login As User

AspNetZero v8.4, Abp 5.5, Mvc/jQuery

Description of Issue An issue came up where a user had been granted permission to login as a tenant but was NOT granted permission to login as a user. The UI correctly presented the user with the opportunity to login as one of the tenant's users, but then failed immedaitely when they selected a target user account from the list.

Assessment It seems like tenant impersonation is useless without user impersonation. So, how is tenant impersonation really supposed to work? I can think of two ways.

Option #1 - treat tenant impersonation as a broader form of user impersonation. Whereever user impersonation rights are allowed, also allow tenant impersonation. The net effect is that granting tenant impersonation automatically grants user impersonation (regardless of the specific state of user impersonation permission).

Option #2 - treat tenant impersonation as a specific form of user impersonation. When exercising the tenant impersonation right in the absence of the user impersonation right, DO NOT display a list of user accounts, but immediately login as the admin user account of the specified tenant. However, if the user impersonation right is ALSO granted, then display the user list for selection of a specific account to impersonate.

Question What am I missing? Why does the system function the way it does today, where both permissions have to be enabled in order to effectively grant tenant impersonation?

Issue Added To GitHub https://github.com/aspnetboilerplate/aspnetboilerplate/issues/6186

I am going to make a guess at what is happening and why.

The primary difference between the ANZ query and the LinqPad query is that ANZ applies the tenancy filters. That's the "why". Because ANZ needs to apply the tenancy filters, it has opted to use a nested subquery structure which provides the necessary additional WHERE clause.

However, the nested subquery also requires a SELECT clause...which by default would be SELECT *. And that's the "what". ANZ doesn't know what fields to select in the subquery, so it's easier (and safer) just to select them all. That way, no matter which fields are requested by the outer query, those fields will be available.

The ideal solution would be for ANZ to scan the outer query and identify the fields projected from the foreign key, and then include only those fields in the subquery SELECT clause. In the event the outer query does not reference any foregin key field, then the default behavior should be to include only the primary key field; as the primary key field will always be required by the JOIN clause.

So, the original query which did not include any foreign key field (e.g. no Parent fields referenced in the projection) ....

var parentId = 1;
var orgUnits = _organizationUnitRepository
    .GetAll()
    .Where(e => e.Parent.Id == parentId)
    .Select(e => e.DisplayName)
    .ToList();

Should have produced the following code (note the Id field is the only subquery output)...

SELECT [a].[DisplayName]
FROM [AbpOrganizationUnits] AS [a]
LEFT JOIN (
    SELECT [a0].[Id]
    FROM [AbpOrganizationUnits] AS [a0]
    WHERE (tenancyFilter2)
) AS [t1] ON [a].[ParentId] = [t1].[Id]
WHERE (tenancyFilter1) AND ([t1].[Id] = @__p_0)

And when the query does specify a foreign key field, such as this....

            var parentId = 1;
            var orgUnits = _organizationUnitRepository
                .GetAll()
                .Where(e => e.Parent.Id == parentId)
                .Select(e => new { e.DisplayName, ParentDisplayName = e.Parent.DisplayName })
                .ToList();

Then ANZ should see that "Parent.DisplayName" while scanning the outer query and return only that field in addition to the primary key; like the following....

SELECT [a].[DisplayName], [t1].[DisplayName] AS [ParentDisplayName]
FROM [AbpOrganizationUnits] AS [a]
LEFT JOIN (
    SELECT [a0].[Id], [a0].[DisplayName]
    FROM [AbpOrganizationUnits] AS [a0]
    WHERE (tenancyFilter2)
) AS [t1] ON [a].[ParentId] = [t1].[Id]
WHERE (tenancyFilter1) AND ([t1].[Id] = @__p_0)


Hi @ismcagdas,

Thanks for your help.

Adding a projection to the ANZ query to return only the DisplayName field...

var parentId = 1;
var orgUnits = _organizationUnitRepository
    .GetAll()
    .Where(e => e.Parent.Id == parentId)
    .Select(e => e.DisplayName)
    .ToList();

results in the following generated SQL...

SELECT [a].[DisplayName]
FROM [AbpOrganizationUnits] AS [a]
LEFT JOIN (
    SELECT [a0].[Id], [a0].[Code], [a0].[CreationTime], [a0].[CreatorUserId], [a0].[DeleterUserId], [a0].[DeletionTime], [a0].[DisplayName], [a0].[IsDeleted], [a0].[LastModificationTime], [a0].[LastModifierUserId], [a0].[ParentId], [a0].[TenantId]
    FROM [AbpOrganizationUnits] AS [a0]
    WHERE (tenancyFilter2)
) AS [t1] ON [a].[ParentId] = [t1].[Id]
WHERE (tenancyFilter1) AND ([t1].[Id] = @__p_0)

Omitting the projection from the LinqPad query, we use this statement....

 AbpOrganizationUnits.Where(e => e.Parent.Id == 1)

And LinqPad generates the following SQL statement...

SELECT [t0].[Id], [t0].[Code], [t0].[CreationTime], [t0].[CreatorUserId], [t0].[DeleterUserId], [t0].[DeletionTime], [t0].[DisplayName], [t0].[IsDeleted], [t0].[LastModificationTime], [t0].[LastModifierUserId], [t0].[ParentId], [t0].[TenantId]
FROM [AbpOrganizationUnits] AS [t0]
LEFT OUTER JOIN [AbpOrganizationUnits] AS [t1] ON [t1].[Id] = [t0].[ParentId]
WHERE [t1].[Id] = @p0

AspNetZero v8.4, Abp 5.5, Mvc/jQuery.

I noticed that any reference I make to a foreign key property causes the SQL query to use a SELECT * on the foreign key table...not just SELECT field.

This code...

	var parentId = 1;	
	var orgUnits =  _organizationUnitRepository
		.GetAll()
		.Where(e => e.Parent.Id == parentId)
		.ToList();

Generates this SQL.... NOTE A: tenancy filters have been replaced for abbreviation. NOTE B: the first select * is expected, but the LEFT JOIN selection should have been only [a0].[Id]

SELECT [a].[Id], [a].[Code], [a].[CreationTime], [a].[CreatorUserId], [a].[DeleterUserId], [a].[DeletionTime], [a].[DisplayName], [a].[IsDeleted], [a].[LastModificationTime], [a].[LastModifierUserId], [a].[ParentId], [a].[TenantId]
FROM [AbpOrganizationUnits] AS [a]
LEFT JOIN (
    SELECT [a0].[Id], [a0].[Code], [a0].[CreationTime], [a0].[CreatorUserId], [a0].[DeleterUserId], [a0].[DeletionTime], [a0].[DisplayName], [a0].[IsDeleted], [a0].[LastModificationTime], [a0].[LastModifierUserId], [a0].[ParentId], [a0].[TenantId]
    FROM [AbpOrganizationUnits] AS [a0]
    WHERE (tenancyFilter2)
) AS [t1] ON [a].[ParentId] = [t1].[Id]
WHERE (tenancyFilter1) AND ([t1].[Id] = @__p_0)

Using LinqPad with EF 3.1.15

This code...

    AbpOrganizationUnits.Where(e => e.Parent.Id == 1).Select(x => x.Code)

Generates this SQL...

SELECT [t0].[Code]
FROM [AbpOrganizationUnits] AS [t0]
LEFT OUTER JOIN [AbpOrganizationUnits] AS [t1] ON [t1].[Id] = [t0].[ParentId]
WHERE [t1].[Id] = @p0

So, where and why is AbpNetZero or Abp expanding the join selection to include all fields rather than using only the necessary fields?

I don't have any metrics on how this is affecting our performance. But, I assume it is substantial because it affects 99% of our queries.

BTW, I also tried this with a direct dbContext call with the same result.

  var parentId = 1;	
  var contextUnits = _dbContext.GetDbContext().OrganizationUnits
                .Where(e => e.Parent.Id == parentId)
                .ToList();
Showing 1 to 10 of 46 entries