Base solution for your next web application

Activities of "cyklussoftware"

Hello,

I am in the process of upgrading from ASP.NET Zero v7.1.0 with .NET Core 2.2 and Angular to ASP.NET Zero v8.1.0 with .NET Core 3.1 with Angular. The upgrade is going well and I am in the process of updating my queries to be compatible with EF Core 3.

I just came across some strange behavior with the Current Unit of Work and the NotificationPublisher. I'll show example code below:

Announcement is an AuditedEntity.

I have this line in my AnnouncementsAppService.Create(...) method:

await _announcementRepository.InsertAsync(announcement);

Later in the Create method, I have this line which calls a separate DomainService:

await _announcementDomainService.SendToAppropriateUsers(announcement, matchingAliases);

SendToAppropriateUsers is a public async Task. In this method, I run await _notificationPublisher.PublishAsync(......) with some parameters. Notifications are published successfully and I see a web notification. When I look at my dbo.Announcements table in the database, the Announcement is saved, but the CreatorUserId field is NULL. The CreationTime column is correctly set.

I figured out that if I call await CurrentUnitOfWork.SaveChangesAsync(); anywhere before I call await _notificationPublisher.PublishAsync(......), the CreatorUserId is saved correctly (it is not null). I can also remove await _notificationPublisher.PublishAsync(......) and it will save correctly. I tried making my method virtual with the [UnitOfWork] attribute. This resulted in the same behavior as I've described.

This line in the ASP.NET Boilerplate NotificationPublisher.cs file (see line here) saves the Unit of Work the same way I tried, but the Announcement.CreatorUserId column is still NULL.

Do you know why this is only happening when I run await _notificationPublisher.PublishAsync(......) before I've saved the CurrentUnitOfWork?

Thanks!

@maliming Thanks for the clarification

I'll take a look. Thanks!

@maliming Thanks for pointing me to those issues. When it comes to Entity Framework Core, would those changes be considered breaking changes? By breaking change, I mean a change that shouldn't be applied to a database that is currently in production. Or, if I create a new database for a new tenant, would it create an inconsistency between the existing database and the new database?

I am upgrading from ASP.NET Zero v7.1 (.NET Core 2.2 + Angular) to ASP.NET Zero v8.1 (.NET Core 3.1 + Angular) and I noticed that ALL of the migrations (including the Initial Migration) have changed.

I believe the only change to the previous migrations was that each and every column in the Designer.cs files now have .HasColumnType applied to them. Is this considered a major breaking change for upgrading from previous versions of ASP.NET Zero or from EF Core 2.2 to EF Core 3.1?

Also, for general reference, after generating a migration using the RAD tool, I would manually change the Delete Behavior from CASCADE to RESTRICT. But I never updated the Designer files. Is it necessary to update the designer files or somehow re-create them? Or is it OK to simply update the main Up and Down methods?

Thanks

Hello,

I am using ASP.NET Zero v7.1 with .NET Core 2.2 and Angular.

In my application, we have many Organization entities. Organizations needs their own permission/authorization system so that members of the organization can be given different roles and abilities within the Organization. We refer to the default ASP.NET Zero permission system as the "Global Permission" system and the Organization based permission system as the "Organization Permission" system.

Our Organization Permission system is not meant to be as comprehensive as the Global Permission system. We simply have 4 Permission Levels with a predefined set of abilities. For example:

  • The Level 3 Permission Level can add Members and modify Organization details like it's logo and description.
  • The Level 2 Permission Level can create/update/delete Announcements or other Organization specific entities unrelated to Member management
  • The Level 1 Permission Level has no special permissions, but can see Organization specific entities assigned to them (or other private Organization information)
  • The "Public" Permission Level doesn't actually exist. When a user is in the "Public" Permission Level, it means that a user doesn't belong to the Orgnaization and can only see Organization specific entities marked as "IsPublic"

Orgnanization specific entities like Announcement can be marked as IsPublic OR be assigned to 1 or more Permission Levels.

Now, when a User wants to see a list of Announcements for an Organization, we query the database for all Announcements related to Organization 1 and check 4 things in the WHERE condition of our query:

  • Does the User have any Global Permissions related to Announcements? If yes, then he can view ALL Announcements
  • Is the Announcement Public? If yes, then he can view the Announcement
  • Does the User have a Level 2 or higher Permission Level for the Organization that this Announcement belongs to? If yes, then he can view the Announcement.
  • Is the Announcement assigned to his Permission Level in the Organization? If yes, then he can view the Announcement.

Checking the 4 conditions in the WHERE of our query works, but it requires us to query for a list of the User's Memberships before doing the Announcement query so that the Announcement query can check the User's permission for each Announcement without doing extra JOIN / GROUP BY logic. I am working to make this easier and more efficient by using a TypedCache and a helper class.

When it comes to creating/updating/deleting Announcements, a User must have Global Permission OR Level 2 or higher for the Organization. Right now, this is done doing another query inside of each Create/Update/Delete method (I already have a helper class doing it), but I would like to abstract the permission checking logic into an Attribute or Filter, if possible, so that I don't have to write as much code each time I create a new create/edit/delete endpoint.

Hopefully what I have described makes sense to some degree. Based on what I've described, do you think it's worth trying to go down the custom Authorization Filter / Authorization Policy route for create/update/delete? Or do you think it makes sense to keep querying like I am because it is a non-standard Authorization scheme?

Thanks!

I am using ASP.NET Zero v7.1 with ASP.NET Core and Angular.

I am looking into adding a Unique Constraint to a custom column I added the AbpUsers table. It is a string column with nvarchar(256), like Username. While I was looking through the table to see how the Username column's uniqueness is enforced, I realized that it isn't enforced directly in the database. I am able to insert duplicate rows if I insert directly using SQL. If I try to create a duplicate email or username when registering through the application, I get the error that says it is a duplicate.

This leads me to believe that uniqueness is checked somewhere in the UserManager when creating the user. Is this a correct assumption? Is there a specific reason that unique contraints aren't enforced in this table (and maybe others too)?

I would love to know the reasoning behind this decision so that I can learn from it. Is it because it is a multi-tenant application? Maybe because the AbpUsers table uses soft-delete? I've been adding unique indexes to my custom database tables so that duplicates are strictly forbidden. With the unique indexes I can't accidentally create duplicate data if I introduce a bug in my code. My tables don't have soft-delete so I haven't run into issues with that. And I haven't run into issues with uniqueness across tenants because my unique indexes refer to Entity row Ids (rows in my many-to-many relationship tables needed unique constraints, for example). I suppose that the unique constraint could cause problems if I try to scale a single tenant across multiple databases, but I don't know if that is possible with ASP.NET Zero.

Is there a reason why I shouldn't be creating unique indexes in a multi-tenant application?

I'm looking forward to hearing your thoughts!

@chauey We pushed custom branding futher back in our backlog, so I haven't worked on this tool for a while. I think the biggest thing it needs is a way to export a tenant-specific CSS file for them to upload.

Feel free to expand or even rewrite the existing logic.

@ismcagdas

I wasn't trying to complain about the number of colors. I was just saying that my tool doesn't consolidate colors that are very similar in color value. I played around with consolidating colors based on the HSV color space, but the version I put on GitHub doesn't do that. It is a potential improvement for my tool.

I wanted to post here to let people know that I made a tool that helped me with my Tenant Custom CSS problem. I am hoping that someone else might find it useful.

I've seen some other posts asking about how to change the branding for individual Tenants. I'm not sure if this is the best place to post this, but over the past 2 days I have been looking into and working on changing themes for ASP.NET Core + Angular v6.5. I might have a solution that will work for some people.

I created a tool that programatically edits the Default theme's CSS files by comparing two different included themes (Default and Pink, for example) and determining which colors are shared and which colors are unique between the two. The CSS that is output works based on CSS variables (https://www.w3schools.com/css/css3_variables.asp). This means that there is a only a single place that colors are defined. With this system, Tenants can upload a CSS document that contains updated variable definitions to reflect their primary branding color. These updated variables overwrite the original variables that the tool outputs, so the web page uses the Tenant colors instead of whatever default color you use.

The tool will generate the entire theme (similar to the Metronic Theme Changer here https://github.com/aspnetzero/metronic, but it doesn't require any new scss to be compiled via Gulp. The outputed Default theme contains the variable references needed to implement branding for individual Tenants. As mentioned, Tenants can change their colors if they upload a CSS document with overriding variable definitions.

I think this is the solution I am going to use. As far as I know, the "official" method might require creating a new theme for every Tenant and baking it directly into the application by default. I could be wrong and there might be a better way to do things. Either way, this was an interesting project to work on for a few days.

You can find the source code (C#) here: https://github.com/Connor14/CSSThemeHelper. Feel free to contribute where you see fit. The tool doesn't minify the css and it doesn't output a dedicated tenant CSS document without overwriting the output files that you specify. It also doesn't consolidate the large number of colors that there are. I have found 62 colors, but I think I forgot to include a CSS file from somewhere. The Metronic Theme Changer project provided by ASP.NET Zero has 69 colors listed and the files that I am pulling from only contained 62.

I hope this helps someone!

Showing 1 to 10 of 42 entries