If you're creating a bug/problem report, please include followings:
'/libs/datatables.net-bs5/css/dataTables.bootstrap5.css'
for the bundle 'Lepton.Global'!abp suite update --preview
then proceed to start it using abp suite
once the browser is opened error 500 is displayed and the exception listed above is visible in the console window that launched ABP Suite.I have also updated the npm module of datatables.net-bs5 globally, but I presume the application is expecting this lib to be available from a local perspective.
If there is a more suited place to report v5 specific issues that would be great as the current support forum is less than accomodation filtering out the v5 specific issues.
PS. If this is a known issue, I'd like to request a credit refund of our question limit, tyvm :)
Update: I was able to add the library to the presumable path it is looking for: %USERPROFILE%\.dotnet\tools\.store\volo.abp.suite\5.0.0-beta.3\volo.abp.suite\5.0.0-beta.3\tools\net6.0\any\wwwroot\libs\
and added the bs5 package there copied from the npm folder, but it now refers to another missing library: Could not find the bundle file '/libs/bootstrap/js/bootstrap.enable.tooltips.everywhere.js' for the bundle 'Lepton.Global'!
Update 2: I've add an empty file called bootstrap.enable.tooltips.everywhere.js
in that folder and abp suite now at least launches, seemingly without any console errors. So we can continue our day, hope this information can contribute to locating the issue, or perhaps if I missed something when upgrading.
Thanks a lot for your time and efforts!
Thanks alot for the quick reply and action, looking forward :)
The entire ABP Suite cannot handle file scoped namespaces, so that is a bigger issue. With the move to .NET 6 for ABP v5 it would be essential to have this working, e.g. after refactoring your code Suite does not understand it at all.
As an added feedback on the enums, if the enum is not of type int, everything else fails, this also applies to flagged enums. I am not sure if ABP Suite CRUD generator supports flags enums but I had to refactor it to explicit int values to work.
In addition, the enum values are added to JSON as: Enum:EnumType:EnumValue
but if you look at https://docs.abp.io/en/abp/5.0/UI/AspNetCore/Tag-Helpers/Form-elements#label-localization-1 it says it looks in the localization texts with EnumType.EnumValue
so right now I have untranslated enums everywhere.
Note: It would be very helpful to have a separate v5 issues thread like exists for v4.4 Note: If these issues should be posted on Github instead let us know :)
Thank you @albert!
I would like to create a computed property for the purpose of using this in the navigation dropdown as the display property. I think this scenario could be pretty common, especially with the desire to often add the IdentityUser as a navigation property to one of your own entities. So for that purpose I wanted to add a "FullName" property to IdentityUserDto, instead of now only being able to choose between Name and Surname that is available on this type (other properties are of course available, but not relevant to the question).
Having already been successful in adding multiple 'regular' properties to the IdentityUser entity and the IdentityUserDto class, I am thus familiar with the various options, and also what is currently available as information from the documentation. However, how could we achieve this type of extension property? I am unable to figure out how to access the Dto instance itself when providing the configuration, so I am confused how I can create my property.
As a reference, this is how I would declare the property if the Dto class was my own:
public string FullName => string.Join(" ", string.Empty, Name, Surname).Trim();
or as a common variation using interpolation:
public string FullName => $"{Name} {Surname}).Trim();
What do I need to provide to the configuration parameter to achieve the equivalent result?
user.AddOrUpdateProperty<string>(UserConsts.FullName, options => options.DefaultValue = <What should I use here?>);
I also looked at the DefaultValueFactory but this Func does not provide access to the referencing type.
Note: If this is currently not possible through the regular extension methods, is there any alternative or other work-around to achieve something similar?
Thanks for your time and effort.
ABP: v5.0.0-rc.1 CLI: v5.0.0-rc.1 Suite: v5.0.0-rc.1
When (re)generating the the various files using the CRUD generator the <MyModule>EntityFrameworkCoreModule class will:
using Acme.Abp.Crm.Contacts;
using Acme.Abp.Crm.Companies;
using Acme.Abp.Crm.Companies;
using Acme.Abp.Crm.Companies;
using Acme.Abp.Crm.Contacts;
using Acme.Abp.Crm.Companies;
options.AddRepository<Company, Companies.EfCoreCompanyRepository>();
options.AddRepository<Contact, Contacts.EfCoreContactRepository>();
options.AddRepository<Company, Companies.EfCoreCompanyRepository>();
options.AddRepository<Company, Companies.EfCoreCompanyRepository>();
options.AddRepository<Company, Companies.EfCoreCompanyRepository>();
options.AddRepository<Contact, Contacts.EfCoreContactRepository>();
Edit: This will happen after an Entity is updated (more properties, extra navigations) and then Save and generate is clicked, or when you simply click it twice. When you then switch to another entity that gets updated or otherwise and click Save and generate then this one will be repeated as well.
@albert,
The same thing happens for the <MyModule>ApplicationAutoMapperProfile
class. I am regenerating the code, since I made changes to the templates, and the mapping code is then duplicated.
Situation after first code generation:
CreateMap<OrganizationUnit, LookupDto<Guid>>().ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.DisplayName));
CreateMap<IdentityUser, LookupDto<Guid?>>().ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Email));
Method contents after second code generation:
CreateMap<OrganizationUnit, LookupDto<Guid>>().ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.DisplayName));
CreateMap<IdentityUser, LookupDto<Guid?>>().ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Email));
// This is duplicated
CreateMap<IdentityUser, LookupDto<Guid>>().ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Email));
As you can observe, it seems that the CRUD generator does not "see" that this mapping already exists? Similarly, this also happens with other mapping statements, where you would end up with multiple definitions when you regenerate the code for multiple entities in succession (still well within regular usage of the CRUD tooling I would say):
CreateMap<Company, CompanyDto>();
CreateMap<CompanyWithNavigationProperties, CompanyWithNavigationPropertiesDto>();
CreateMap<Company, LookupDto<Guid?>>().ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Name));
// This is duplicated
CreateMap<Company, CompanyDto>();
Edit: The namespace duplication happens a lot as well for the *AppService
classes and the classes *WithNavigationPropertiesDto
.
(though I do realize this is minor and will not actually produce error situations)
Hope this explains it well enough, there are other parts where the Regex seems to be too greedy and will match too much code, even code the CRUD generator did not create, but I moved that code out to separate files to avoid it all to gether.
Thanks again for your time and effort.
Hi bolenton,
(Note: Making some assumptions here about the version of ABP that you are using, so I am going to assume v4.4 or v5.0 for the sake of my answer)
Disclaimer: Though my answer may work for your particular case, please don't view this as an official response from Support nor the proper and/or intended work-around to address this particular issue.
Whenever you create a new Tenant, there is an event handler class that is fired inside your project/module: AcmeTenantDatabaseMigrationHandler
(where Acme is your project/module name).
Inside this class there is a method sequence that is being called whenever a new Tenant is created:
public async Task HandleEventAsync(TenantCreatedEto eventData)
This is called when you create a Tenant from inside the SaaS management screens. Inside it makes a call to the MigrateAndSeedForTenantAsync
method that does a few things:
There seems to be however a small issue, though perhaps unrelated, when [v] Use Shared Database is used, the code should not try to find an associated connection string for the newly created Tenant. When it cannot find the connection string, it tries to resolve an invalid database connection, which subsequently times out.
Though personally not sure how it fixes it, inside the MigrateAndSeedForTenantAsync
method you can see the usage of the Unit of Work that is responsible for commiting your Tenant admin user:
// Seed data
using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
{
await _dataSeeder.SeedAsync(
new DataSeedContext(tenantId)
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, adminEmail)
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, adminPassword)
);
await uow.CompleteAsync();
}
Here you can see that requiresNew: true
is being used, which if I understand it correctly, launches a new Unit of Work, which then cannot resolve the connection string of the current Tenant, and failes (due to time out). By settings this value to false, the "current" connection string of the existing Unit of Work is used, and thus the admin user is created as expected.
using (var uow = _unitOfWorkManager.Begin(requiresNew: false, isTransactional: true))
Hope this helps.
Hi,
In our application we have the following structure: Main Application -> Our Custom Module -> ABP Module(s). Inside this custom module, we want to be able to link our data with a User and/or Organisation. So through ABP Suite we have setup the needed navigation properties:
This took some effort, and if we didn't have the Commercial source package, I am not sure how to have accomplished this properly from inside a module, but that is besides the issue I am raising. Looking at the provided sample code generated within the AcmeDbContext
class, we can see that [ReplaceDbContext(typeof(IIdentityProDbContext))]
is used to replace that implementation with that of our own AcmeDbContext class.
Assuming this is how it is done, we did the same in our ModuleDbContext class and added the [ReplaceDbContext(typeof(IIdentityProDbContext))]
attribute. We added the required DbSet
properties and made sure that builder.ConfigureIdentityPro();
is called. We then added [ReplaceDbContext(typeof(IAcmeDbContext))]
attribute to the main application AcmeDbContext so we can also expose our new entities there for later use (a call to builder.ConfigureAcme();
was also added as we replicated that setup).
We have a Company entity that we wish to connect to a User and Organization and using the Suite configuration above it generates the following class:
public partial class Company : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid OrganizationId { get; set; } // OrganizationUnit is referenced here as expected
public Guid UserId { get; set; } // IdentityUser is referenced here as expected
public Guid? TenantId { get; set; }
/* Snip! */
}
Subsequently, the WithNavigationProperties class is generated as expected (putting it here for reference and completeness sake):
public class CompanyWithNavigationProperties
{
public Company Company { get; set; }
public OrganizationUnit OrganizationUnit { get; set; }
public IdentityUser IdentityUser { get; set; }
}
And the necessary methods within the EfCoreCompanyRepository class is also available:
protected virtual async Task<IQueryable<CompanyWithNavigationProperties>> GetQueryForNavigationPropertiesAsync()
{
return from company in (await GetDbSetAsync())
join organizationUnit in (await GetDbContextAsync()).OrganizationUnits
on company.OrganizationId equals organizationUnit.Id into organizationUnits
from organizationUnit in organizationUnits.DefaultIfEmpty()
join identityUser in (await GetDbContextAsync()).Users
on company.UserId equals identityUser.Id into users
from identityUser in users.DefaultIfEmpty()
select new CompanyWithNavigationProperties
{
Company = company,
OrganizationUnit = organizationUnit,
IdentityUser = identityUser
};
}
We build and start the application and navigate to the 'Companies' menu item; here we are treated with the following:
Value cannot be null. (Parameter 'inner')
message inside the EF Core logAfter a slight refactoring of this particular method (as I wanted to get more familiar with the generated code and why it looked like that) it looks like the following:
protected virtual async Task<IQueryable<CompanyWithNavigationProperties>> GetQueryForNavigationPropertiesAsync()
{
// Retrieve the current DbContext once (does it make a difference?)
var dbContext = await GetDbContextAsync();
// Add explicit references to the DbSet properties so we can easily inspect them during Debug
var companies = dbContext.Companies; // This is (await GetDbSetAsync()) in generated code
var organizations = dbContext.OrganizationUnits; // This DbSet<T> is null!
var identityUsers = dbContext.Users; // This DbSet<T> is null!
return from company in companies
// The generated join is an 'Outer Join' even though our navigations are 'Required', but irrelevant to the issue
join organizationUnit in organizations
on company.OrganizationId equals organizationUnit.Id into organizationUnits
from organizationUnit in organizationUnits.DefaultIfEmpty()
// The generated join is an 'Outer Join' even though our navigations are 'Required', but irrelevant to the issue
join identityUser in identityUsers
on company.UserId equals identityUser.Id into users
from identityUser in users.DefaultIfEmpty()
select new CompanyWithNavigationProperties
{
Company = company,
OrganizationUnit = organizationUnit,
IdentityUser = identityUser
};
}
Now I could see that the DbSet properties that we took from the IIdentityProDbContext
interface are seemingly not properly referring, or mapped to the table on this particular context, even though by my current, and still very much growing understanding of ABP, was configured correctly using [ReplaceDbContext(typeof(IIdentityProDbContext))]
?
TL;DR, after using ReplaceDbContext
the implemented DbSet properties are null
inside a module that the main application is using.
What am I missing here? Why is Users and Organization null? Did I miss some key configuration part to make this work? Did I totally misunderstand the intention here? All the wiring of this logic is happening inside the Module and not the Application. We will make and use different pages and logic there that does not belong to this module. I have also checked the various sources of the Commercial modules, but it seems nothing is making a reference to either a User or Organization.
To summarize:
null
Not sure how to create a "simple" project that still reflects the setup and intent that we want to use within our Application. But since the project contains very little IP, I have no issue sharing the solution in its entirety.
Could this be transferred to you in a secure manner?