Activities of "alexander.nikonov"

Hi, I've deployed boilerplate projects (HttpApi.Host and IdentityServer) to Azure VM.

Both are placed under "Default Web Site" as applications. While I was able to resolve a path issues for HttpApi.Host application and it is displayed properly in browser, I still get a broken markup for IdentityServer application (as shown in the screenshot below): probably, it has to do with incorrect application paths for bundle creation - they are considered for web root folder, not for application folder. Could you please help me to fix this? Are there other places in the solution I might need to check and make the changes for setting correct root folder (i.e. application folder is a root folder, not a web site root folder).

I have not tried to deploy to local IIS server yet - in fact, it's even not set up, because we supposed to use remote DEV machine for running our backend. BTW, when I run this app via IIS Express - I can't see the 'Lepton.Global' bundle loading at all. I've tried to figure out what this bundle was about, playing around AbpBundlingOptions in my IdentityServerModule and hoping to override LeptonThemeBundles.Scripts.Global / LeptonThemeBundles.Styles.Global bundles - but seems like those are not bundle names...

I hope it will be easy... :) Step 1) Create a boilerplate project, as described in the abp doc; Step 2) Create the folder on a remote machine for publishing IdentityServer project, set it up as an IIS application under Default Web Site and assign a separate app pool to it: Step 3) Publish IdentityServer project to that remote machine using the publish profile as below (please replace XXX / YYY / ZZZ and some other dummy values with relevant values):

IdentityServer_to_RemoteMachine.pubxml

<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <PublishProvider>AzureVirtualMachine</PublishProvider>
    <LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish>http://XXX.cloudapp.azure.com/YYY.IdentityServer</SiteUrlToLaunchAfterPublish>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <ProjectGuid>073c361e-b8f4-49f5-93cc-72a3ff49c026</ProjectGuid>
    <MSDeployServiceURL>XXX.cloudapp.azure.com:8172</MSDeployServiceURL>
    <DeployIisAppPath>Default Web Site\YYY.IdentityServer</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <EnableMSDeployBackup>True</EnableMSDeployBackup>
    <UserName>YOUR_USERNAME</UserName>
    <_SavePWD>True</_SavePWD>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <SelfContained>false</SelfContained>
  </PropertyGroup>
</Project>

IdentityServer_to_RemoteMachine.pubxml.user

<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <TimeStampOfAssociatedLegacyPublishXmlFile />
    <EncryptedPassword>YOUR_ENCRYPTEDPASSWORD</EncryptedPassword>
  </PropertyGroup>
  <ItemGroup>
    <DestinationConnectionStrings Include="Default">
      <Value>YOUR_CONNECTIONSTRING</Value>
    </DestinationConnectionStrings>
  </ItemGroup>
</Project>

Step 3) When the published IdentityServer website is being run, you (hopefully) will see the same broken UI as reported by me earlier;

Hi,

we are not planning to use DB migration for our project tables - we have predefined DB tables structure, which will be changed by applying SQL scripts and the code just must be in-sync with it. At the same time, we understand DB migration mechanism is used in ABP Framework solution to create default tables (ABP[XXX], IDENTITYSERVER[XXX]) - so when ABP Framework gets updated, these tables might be updated accordingly.

Could you please suggest the best approach to use in the solution? Is it possible to go without DbMigration-related projects at all? Or it needs to be some selective migration? How to set it up?

I've tried that (I used EntityFrameworkCore project as a base and added the Microsoft.EntityFrameworkCore.Design reference as asked by the tool). After running migration tool, I'm getting

Unable to create an object of type 'XXXXDbContext' //this is the class inherited from AbpDbContext<XXXXDbContext>

I see now... But is dotnet-ef tool the same thing as Microsoft.EntityFrameworkCore.Tools package? So basically I can use PM commands add-migration / script-migration -from 0 and so on instead? I just need to leave only ABP-related tables inside CentralToolsMigrationsDbContext.OnModelCreating method.

I wonder why UpdateAsync returns INPUT data, even if the input data has been changed? For instance, I have the space truncating rule for my entity:

b.Property(x => x.Domain)
    .HasConversion(new ValueConverter&lt;string, string&gt;(v => v.Trim(), v => v.Trim()))
    .HasColumnName("C_DOMAIN").HasMaxLength(DbConsts.DomainMaxLength);
                

Despite this fact, the method returns INPUT data which does not have truncation:

public async Task&lt;ModuleDto&gt; UpdateAsync(ModuleKeyDto id, UpdateModuleDto input)
{
    var module = await _moduleRepository.GetAsync(m => m.ApplicationId == id.ApplicationId && m.ModuleId == id.ModuleId);
    if (module == null)
    {
        throw new UserFriendlyException("Module not found", ErrorCodes.NotFound);
    }
    ObjectMapper.Map(input, module);
    var updatedModule = await _moduleRepository.UpdateAsync(module); // updatedModule container non-truncated value that was present in module!!!
    await CurrentUnitOfWork.SaveChangesAsync();
    return ObjectMapper.Map&lt;Module, ModuleDto&gt;(updatedModule);
}

Looks like a bug? I can re-read the entity and it then will look allright, but don't want to make an extra trip to DB.

OK, thanks, now I know the property values not changed, but parameter values instead. But what do you mean by DTO normalization? Carry on some operations on DTOs? But if so - it's easier just to re-read the entity to get correct up-to-date values.

Saying honestly, not a very good solution, imho. Usually DTO is just a bunch of fields and should not contain some functionality. Handling all the logic in OnModelCreating looks fine and logical. So - since it's not possible to get new data straight from update object - I think I will have to re-read the entity.

  • ABP Framework version: v2.9
  • UI type: Angular
  • Tiered (MVC) or Identity Server Seperated (Angular): Identity Server Seperated

Hi. To extend the existing ABP tenants / users functionality in the system, we had to create corresponding tables / entities which relate 1:1 to ABP one:

 //Our Tenant class
 using AbpTenant = Volo.Saas.Tenant;

 namespace XXX.Tenants {
      public class Tenant : LogEntity {
      ...
      public Guid AbpId { get; set; }
      public AbpTenant AbpTenant { get; set; }`
      ...
      
 //OnModelCreating   
        ...
        builder.Entity<Tenant>()
            .HasOne(x => x.AbpTenant)
            .WithOne()
            .HasPrincipalKey<Volo.Saas.Tenant>(x => x.Id)
            .HasForeignKey<Tenant>(x => x.AbpId);
        ...
        builder.Entity<Tenant>(b => {
            b.ToTable("OUR_TENANT");
            b.ConfigureByConvention();
            b.HasKey(x => x.Id);
            b.Property(x => x.Id).HasColumnName("C_TENANT").IsRequired().ValueGeneratedNever();
            ...
            b.Property(x => x.AbpId).HasColumnName("C_ABP_TENANT").IsRequired();
            ...

We had to create own client-side infrastructure in Angular app as well, to process these composite entities:

//Angular Tenants model

export interface State {
    tenants: Response;
    tenantsLookup: Common.LookupResponse<number>;
}

export interface Response {
    items: Tenants.TenantWithNavigationProperties[];
    totalCount: number;
}

export interface TenantsQueryParams extends ABP.PageQueryParams {
    filterText?: string;
    idMin?: number;
    idMax?: number;
    shortName?: string;
    fullName?: string;
    companyId?: number;
    masterId?: number;
    abpId?: string;
    isMaster?: boolean;
}

export interface AbpTenant {
    id: string;
    name: string;
    editionId: string;
}

export interface AbpTenantCreateDto {
    name: string;
    editionId: string;
    adminEmailAddress: string;
    adminPassword: string;
}

export interface AbpTenantUpdateDto {
    name: string;
    editionId: string;
}

export interface TenantWithNavigationProperties {
    id: number;
    shortName: string;
    fullName: string;
    comment: string;
    companyId: number;
    masterId: number;
    abpId: string;
    isMaster: boolean;
    abpTenant: AbpTenant;
}

export interface TenantCreateDto {
    id: number;
    shortName: string;
    fullName: string;
    comment: string;
    companyId: number;
    masterId?: number;
    abpId?: string;
    isMaster: boolean;
    abpTenant: AbpTenantCreateDto;
}

export interface TenantUpdateDto {
    id: number;
    shortName: string;
    fullName: string;
    comment: string;
    companyId: number;
    masterId?: number;
    abpId: string;
    isMaster: boolean;
    abpTenant: AbpTenantUpdateDto;
}

To circumvent potential issues with compatibility, we have decided to handle CRUD operations using two repositories - ABP ITenantRepository and IIdentityUserRepository. Unfortunately, it raised a major transaction issue: row lock when trying to create (not tested thouroughly on other operations, but sure the issue exists there as well) a new tenant. We have tried different approaches (including using ITenantAppService directly instead of ITenantRepository) to resolve it, but none of them worked:

            //using ITenantAppService
            using var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true);
            var abpTenantDto = await _abpTenantAppService.CreateAsync(input.AbpTenant);
            var tenant = ObjectMapper.Map<CreateTenantDto, Tenant>(input);
            tenant.AbpId = abpTenantDto.Id; //causes row lock SOMETIMES
            var newTenant = await _tenantRepository.InsertAsync(tenant);
            await uow.CompleteAsync(); //this operations hangs SOMETIMES because of row lock
            return ObjectMapper.Map<Tenant, TenantDto>(newTenant);
            
            //using ITenantRepository
            using var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true);
            var abpTenant = await _abpTenantManager.CreateAsync(input.AbpTenant.Name, input.AbpTenant.EditionId);
            input.AbpTenant.MapExtraPropertiesTo(abpTenant);
            var newAbpTenant = await _abpTenantRepository.InsertAsync(abpTenant);
            var tenant = ObjectMapper.Map<CreateTenantDto, Tenant>(input);
            tenant.AbpId = abpTenant.Id; //causes row lock ALWAYS
            var newTenant = await _tenantRepository.InsertAsync(tenant);
            await uow.CompleteAsync(); //this operations hangs ALWAYS because of row lock
            return ObjectMapper.Map<Tenant, TenantDto>(newTenant);
            

Now we use two separate commits (ABP tenant, then - our tenant) as a workaround (deleting the first entry if first commit failed), which of course is not good at all and is just a temporary solution:

            #region ABP tenant commit
            using var abpTenantUow = _unitOfWorkManager.Begin(requiresNew: true);
            Tenant newTenant = null;
            var abpTenant = await _abpTenantManager.CreateAsync(input.AbpTenant.Name, input.AbpTenant.EditionId);
            input.AbpTenant.MapExtraPropertiesTo(abpTenant);
            var newAbpTenant = await _abpTenantRepository.InsertAsync(abpTenant);
            await abpTenantUow.CompleteAsync();
            #endregion ABP tenant commit

            #region Tenant commit
            using var tenantUow = _unitOfWorkManager.Begin(requiresNew: true);
            var tenant = ObjectMapper.Map<CreateTenantDto, Tenant>(input);
            tenant.AbpId = abpTenant.Id;
            newTenant = await _tenantRepository.InsertAsync(tenant);
            tenantUow.Failed += async (sender, args) =>
            {
                using var abpTenantDeleteUow = _unitOfWorkManager.Begin(requiresNew: true);
                await _abpTenantRepository.HardDeleteAsync(abpTenant);
                await abpTenantDeleteUow.CompleteAsync();
            };
            await tenantUow.CompleteAsync();
            #endregion Tenant commit

Here is the sessions screenshot displaying the row lock when trying to use one transaction:

Could you please help us to resolve transaction issue in the first place and also suggest how to handle two-tenant-approach in the most correct way on both back-end and front-end side?

Showing 1 to 10 of 267 entries
Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11