Base solution for your next web application

Activities of "compassinformatics17"

  • What is your product version?: 4.8.1
  • What is your product type: MVC
  • What is product framework type: .NET Framework (Non Core)

Issue

  • Dates are stored in the SqlServer in ISO format YYYY-MM-DD HH:MM:SS
  • But query results return the dates pre-localised and not in ISO format as one would expect. This is not at all ideal since I would like to use the ISO date as a starting point for date transformations on the client side.
  • I have tried many approaches to get the framework to return the date in raw ISO form but none work, the results always seem to be pre-localised in the debugger and also in the JSON response.
  • I have also put this question on StackOverfllow last year: https://stackoverflow.com/questions/62016070/asp-net-abp-return-an-iso-datetimenot-local-from-the-db
  • Is there a way to make the framework default to ISO date time formatting rather than localising all results.

Things I have tried

  • Setting the DateTimeKind
  • Disabling DateTime Normalisation
  • Setting JSON parameters in Global.asax Application_Start() such as:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
  • Commenting out "Clock.Provider = ClockProviders.Utc" in Global.asax.
  • Forcibly converting to ISO format using date.ToString('yyyy-MM-dd').
    • Cannot do this because the LINQ query is not aware of ASP date functions since comparable functions do not exist in SQL and hence the query will fail and throw and error.

Example of the issue

var query = _someRepository.GetAll();

var rows = query.Select(a => new RowViewModel { Id = a.Id, Name = a.Name, CreatedBy = a.CreatorUser.Name + " " + a.CreatorUser.Surname, CreatedOn = a.CreationTime //Returns 22/05/2020 10:05:05, I want 2020-05-22 10:05:05 });

Hi All,

We are deploying our product which is AspNetZero Core & Jquery 8.9.2 to Azure. We have encountered an issue with environment configuration. ASPNETCORE_ENVIRONMENT has been set to Production in Azure and I have verified that it is in effect via Kudu. But AspNetZero is still operating in Development mode and returning full stack traces for any errors. I wonder if there is something that I am missing. We have not made any customisations which should affect this functionality.

The only changes we have made to the dev, prod process is that we only use a single appsettings.json file and our deployment process auto replaces the values in this file dependant on the environment into which we are deploying via DevOps. But to my knowlege this code does not alter or reference ASPNETCORE_ENVIRONMENT in any way. Everything works fine via this setup, but I cannot see why the ASPNETCORE_ENVIRONMENT is being ignored by the project.

AppConfigurations.cs

        private static IConfigurationRoot BuildConfiguration(string path, string environmentName = null, bool addUserSecrets = false)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(path)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            // Disable this, we want to always use appsettings.json for clarity and ease of configuration.
            // appsettings.Production.json and appsettings.Staging.json also removed.
            //if (!environmentName.IsNullOrWhiteSpace())
            //{
            //    builder = builder.AddJsonFile($"appsettings.{environmentName}.json", optional: true);
            //}

            builder = builder.AddEnvironmentVariables();

            if (addUserSecrets)
            {
                builder.AddUserSecrets(typeof(AppConfigurations).GetAssembly());
            }

            var builtConfig = builder.Build();
            new AppAzureKeyVaultConfigurer().Configure(builder, builtConfig);

            return builder.Build();
        }
    }

Hi All, I am trying to access some Azure specific settings in my DbContext. It works in local dev but fails once deployed to Azure or IIS becuase WebContentDirectoryFinder.CalculateContentRootFolder() is apparently for Dev.

I am trying to configure Azure Managed Identity access and I need to edit my context as follows.

public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
            // Check to see if we are attempting to connect to an Azure DB. If we are use Azure Managed Identity to authenticate.
            var dbConnection = Database.GetDbConnection();
            if (dbConnection is SqlConnection connection)
            {
                // If not an Azure db connection, just return, no need to get managed identity token.
                if (!connection.ConnectionString.Contains(_appConfiguration["Azure:azureDatabaseEndpoint"], StringComparison.OrdinalIgnoreCase))
                    return;

                // If an Azure connection but using password, just return, no need to get managed identity token.
                if (connection.ConnectionString.Contains("password=", StringComparison.OrdinalIgnoreCase))
                    return;

                // Get Azure managed identity token for the given AzureServiceTokenProviderConnectionString connection string.
                // https://docs.microsoft.com/en-us/azure/key-vault/general/service-to-service-authentication#connection-string-support
                connection.AccessToken = new AzureServiceTokenProvider(_appConfiguration["Azure:AzureServiceTokenProviderConnectionString"])
                    .GetAccessTokenAsync($"https://{_appConfiguration["Azure:AzureDatabaseEndpoint"]}/")
                    .Result;
            }
        }

I could use DI to pull in IAppConfigurationAccessor as one would normally do but this then causes issues because the framework is designed to use the normal constructor

public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)

I also tried to get them locally in the context as follows, which works in Dev but not on IIS or Azure because of WebContentDirectoryFinder.CalculateContentRootFolder() being Dev only functionality.

// Enable access appsettings.json in the context.
private readonly IConfigurationRoot _appConfiguration = AppConfigurations.Get(
                                                                    WebContentDirectoryFinder.CalculateContentRootFolder(),
                                                                    addUserSecrets: true
                                                                    );

Is there some way in which I can access the appsettings.json from within the context other than this or is there a way for me to get the correct path for the root directory of MVC?

Thanks, Barry.

Hi Guys,

I am coming to the end of the dev stage of an application and want to deploy to QA/UAT. As part of this process I want to run "npm run build". I followed the process outlined here https://docs.aspnetzero.com/en/aspnet-core-mvc/latest/Setting-Up-an-Azure-Pipeline-Mvc-Core for yarn and "npm run build" in DevOps. As you can see below, "npm run build" executes correctly with no errors, but the created bundles are identical to the dev versions and are not minified. I notice that this was a bug in the past #6987. Is it possible this bug has reappeared?

I'm using version 8.9.2 of the framework.

Thanks, Barry.

2020-08-04T13:43:54.4659893Z [14:43:54] Using gulpfile H:\Agents\1\_work\67\s\src\MyProject.Web.Mvc\gulpfile.js
2020-08-04T13:43:54.4721122Z [14:43:54] Starting 'build'...
2020-08-04T13:47:48.3216910Z [14:47:48] Finished 'build' after 3.88 min
2020-08-04T13:47:48.4513226Z ##[section]Finishing: NPM Run Build task

Hi Guys,

I have encountered some unusual behaviour in the framework and I'd really appreciate some advice in working around it.

I have a service which takes in a csv file, converts to entities and then saves to the db.

For negative testing I was doing the following:

  1. Purposely import a decimal which is too long for the db field.
  2. Catch the error, and return a nicely formatted error to the user.
  3. But the error never returns, instead a 500 Internal error is returned to the user.

I understand from #5997 that this is likely caused by SaveChanges() auto executing upon existing the function. I am catching the internal error but another is then thrown by the auto-execution.

I have tried to use transaction scopes and also a local unit of work but both fail because the operations performed in CreateOrUpdateEnquiry() cannot attach to the local transaction scope. Is there something I am missing here? Is there some way for me to use a local unit of work here so that upon exiting its scope the global context does not contain any unsaved changes?

I do have a working solution as seen below which just scans the context and undoes changes RejectChanges(). But is there a more elegant/efficient way to do this?

Thanks, Barry.

private async Task<ImportedEnquiriesReportDto> CreateEnquiries(List<ImportedEnquiryDto> importedEnquiries)
        {
            _enquiryDictionary = await GetEnquiryDictionary();
            _enquiryStageDictionary = await GetEnquiryStageDictionary();
            _enquiryTypeDictionary = await GetEnquiryTypeDictionary();
            _surveyTypeDictionary = await GetSurveyTypeDictionary();

            var report = new ImportedEnquiriesReportDto();
            List<Enquiry> enquiriesToCreate = new List<Enquiry>();

            try
            {
                _ctx.ChangeTracker.AutoDetectChangesEnabled = false; // Get context and disable EF auto-detect changes. HUGE speed increase!!

                foreach (var importedEnquiry in importedEnquiries)
                {
                    if (importedEnquiry.CanBeImported())
                    {
                        try
                        {
                            // New enquiries are created and added to the enquiriesToCreate list.
                            // Existing enquiries are updated in CreateOrUpdateEnquiry() and picked up by the _ctx.ChangeTracker.DetectChanges() call below.
                            // Newly created enquiries will have a detached state at this point and hence can be distinguished from updates, unchanged etc.
                            var enquiry = await CreateOrUpdateEnquiry(importedEnquiry);
                            if (_ctx.Entry(enquiry).State == EntityState.Detached)
                            {
                                enquiriesToCreate.Add(enquiry);
                            }
                        }
                        catch (Exception)
                        {
                            report.InvalidEnquiries.Add(importedEnquiry);
                        }
                    }
                    else
                    {
                        report.InvalidEnquiries.Add(importedEnquiry);
                    }
                }

                // Add new enquiries in bulk. Only call detect changes once after adding all new enquiries.
                if (enquiriesToCreate.Any())
                {
                    await _ctx.Enquiries.AddRangeAsync(enquiriesToCreate);
                    enquiriesToCreate.Clear();
                }

                // Detect any remaining updates etc.
                _ctx.ChangeTracker.DetectChanges();
                report.EnquiriesCreated = _ctx.ChangeTracker.Entries<Enquiry>().Count(e => e.State == EntityState.Added);
                report.EnquiriesUpdated = _ctx.ChangeTracker.Entries<Enquiry>().Count(e => e.State == EntityState.Modified);
                report.EnquiriesUnchanged = _ctx.ChangeTracker.Entries<Enquiry>().Count(e => e.State == EntityState.Unchanged);
                await _ctx.SaveChangesAsync();
            }
            catch (Exception)
            {
                RejectChanges(); // This works but is there a nicer way??
                throw new UserFriendlyException(L("ErrorSavingToDatabase"));
            }
            finally
            {
                // Turn EF auto detect changes back on.
                _ctx.ChangeTracker.AutoDetectChangesEnabled = true;
            }

            return report;
        }
        
private void RejectChanges()
        {
            foreach (var entry in _ctx.ChangeTracker.Entries())
            {
                switch (entry.State)
                {
                    case EntityState.Modified:
                    case EntityState.Deleted:
                        entry.State = EntityState.Modified; //Revert changes made to deleted entity.
                        entry.State = EntityState.Unchanged;
                        break;
                    case EntityState.Added:
                        entry.State = EntityState.Detached;
                        break;
                }
            }
        }

I have written a customised DomainTenantResolveContributor which works just fine. It allows me to access the host via admin.{domain}.com and my various sub-domain tenants. One thing is escaping me however. I would like to return a 404 page when a tenant is not found. This seems like it should be simple, but no matter what I try in the framework, I cannot seem to kill the request and return a 404 if the tenant is not found. I have tried multiple approaches all with the same result, they just result in being sent to the host login which is certainly not desirable.

            var tenantInfo = _tenantStore.Find(tenancyName);
            if (tenantInfo == null)
            {
                // No tenant found matching the given name, return 404.
                throw new EntityNotFoundException();
            }

I'd really appreciate any advice on how to kill the request and redirect to a 404 here. From my reading .NET core should auto handle these types of exceptions but that is not happening here.

Thanks, Barry.

Hi Guys, This one seems like it should be easy but I cannot seem to find any reference to it in the docs.

I have enabled sub-domain wildcard multi-tenancy and it works just fine as specifed in the docs.

But when it is enabled, how do I then log in as the host user? If I go to the root domain sitename.com, it just defaults to the default tenant. Is there a way to configure a url for the host such as admin.sitename.com?

Thanks, Barry.

Hi, Not specifically a development question but I received a response today to another question which I had asked. I needed to add my GitHub username to our listed GitHub members. I did this and it reported that I had been added successfully. However I did not reveive any email. I also checked current invitations via my GitHub account and I have not received any invitations to accept. Is there currently an issue with the invitations? Thanks, Barry.

Hi,

I am testing minification in ASPNetZero Core. I can run npm run create-bundles without issue but when I run npm run build, I get the error below.

The error seems to be with /view-resources/Areas/App/Views/Common/_KeyValueListManager.js. On line 113 it appears to not be able to handle the period.

[1/4] Resolving packages... success Already up-to-date. Done in 0.61s. [17:19:18] Using gulpfile D:\CompassCoreJqueryDemo\src\CompassCorejQueryDemo.Web.Mvc\gulpfile.js [17:19:18] Starting 'build'... [17:19:33] 'build' errored after 15 s [17:19:33] SyntaxError in plugin "gulp-uglify-es" Message: Unexpected token: punc (.) Details: filename: _KeyValueListManager.js line: 113 col: 36 pos: 4251 domainEmitter: [object Object] domainThrown: false

npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! [email protected] build: yarn && gulp build npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the [email protected] build script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

Thanks, Barry.

Hi All,

I wonder if someone might help to shed some light on an issue which I have noticed. I am putting together a project in AspNetZero Core MVC & Jquery. All is working well except I encountered an issue with the Abp.AutoMapper.

In the documentation there are two AutoMapping methods mentioned:

"CustomDtoMapper.cs is used to create mapping from Person to PersonListDto. FullAuditedEntityDto is inherited to implement audit properties automatically. See application service and DTO documentations for more information. We are adding the following mappings."

And

"We use Abp.AutoMapper library that makes usage of AutoMapper simpler and declarative."

But the example given on the page https://docs.aspnetzero.com/en/aspnet-core-mvc/latest/Infrastructure-Core-Mvc-Dto-Mappings is no longer representative of what is in the codebase, that mapping is now done in CustomDtoMapper.cs.

configuration.CreateMap<Tenant, TenantListDto>();
configuration.CreateMap<TenantEditDto, Tenant>().ReverseMap();

My issue is that Abp.AutoMapper does not seem to work at all from Application.Shared project in any DTO's. For instance if I try to enter the example on the above page in TenantEditDto.cs the Tenant class cannot be resolved.

using System;
using System.ComponentModel.DataAnnotations;
using Abp.Application.Services.Dto;
using Abp.Auditing;
using Abp.AutoMapper;
using Abp.MultiTenancy;

namespace CompassCorejQueryDemo.MultiTenancy.Dto
{
    [AutoMap(typeof(Tenant))]  //Tenant cannot be resolved here. I have tried the same in many other existing DTO's and in my own ones, but all behave the same, the typeof(class) cannot be resolved.

I have checked and Abp.AutoMapper is still used in other models and in the GraphQL DTO's but not in Application.Shared. All DTO's in Application.Shared are now handled by AutoMapper in CustomDtoMapper.cs.

I cross checked with an Asp.Net Zero MVC5 project and it matches exactly with the exception that Abp.AutoMapper works as intended in the MVC5 project.

Is there a reason for this change or am I missing something obvious? Any advice is appreciated.

Regards, Barry.

Showing 1 to 10 of 11 entries