Hello ABP Support,
Following this guide, https://volosoft.com/blog/Migrating-from-ASP.NET-MVC-5.x-to-ASP.NET-Core; we ran into some issues while converting from ASP.NET MVC 5 to the SDK-style project format. After converting our EntityFramework project when running the Migrator tool, we received the following error message (Unable to update database... see screenshot below):
Unfortunately due to the size of the product we are unable to send you the full solution. Is there source code available for the final converted solution from the blog post article? Is there a newer guide that has updated details about the migration process?
Thank you for your time and effort.
@ismcagdas,
Sorry for the late reply. Yes we are using multitenancy. Our work-around is okay we just questioned why we need to override the base class method GetRoleByNameAsync.
public virtual async Task<TRole> GetRoleByNameAsync(string roleName)
{
var role = await FindByNameAsync(roleName);
if (role == null)
{
throw new AbpException("There is no role with name: " + roleName);
}
return role;
}
with
public override async Task<Role> GetRoleByNameAsync(string roleName)
{
return await Task.FromResult(_roleRepository
.GetAll()
.SingleOrDefault(x => String.Equals(x.Name, roleName)));
}
Dear @musa.demir,
Thank your for the reply. While developing this feature I manually added the Administrator role using the SQL Server Mangement Studio Edit Table feature. When I script the data from that table here is that record.
SET IDENTITY_INSERT [dbo].[AbpRoles] ON
INSERT [dbo].[AbpRoles] ([Id], [TenantId], [Name], [DisplayName], [IsStatic], [IsDefault], [IsDeleted], [DeleterUserId], [DeletionTime], [LastModificationTime], [LastModifierUserId], [CreationTime], [CreatorUserId], [NormalizedName]) VALUES (1, 1, N'Administrator', N'Administrator', 1, 1, 0, NULL, NULL, NULL, NULL, CAST(N'2021-01-13T19:01:23.653' AS DateTime), NULL, N'ADMINISTRATOR')
SET IDENTITY_INSERT [dbo].[AbpRoles] OFF
I have since added the framework code, previously commented out by us, from the Entity Framework csproject for TenantDb seeding. That code looks like this:
// Administrator role
var adminRole = _context.Roles.FirstOrDefault(r => r.TenantId == _tenantId && r.Name == StaticRoleNames.Tenants.Administrator);
if (adminRole == null)
{
_context.Roles.Add(new Role(_tenantId, StaticRoleNames.Tenants.Administrator, StaticRoleNames.Tenants.Administrator) { IsStatic = true, IsDefault = true });
_context.SaveChanges();
}
I did alter the constant value from 'Admin' to 'Administrator'; here is that adjustment:
public static class Tenants
{
public const string Administrator = "Administrator";
public const string User = "User";
}
To assign I picked a known user and again used the SQL Management Studio Edit Table feature to populate the table data. In this instance the TenantId was 1, the UserId was 1 and the RoleId was 1 and the CreationTime was now. The equivalent T-SQL code to insert the record would like this:
INSERT INTO AbpUserRoles (TenantId, UserId, RoleId, CreationTime) VALUES (1, 1, 1, GetUtcDate());
Dear Support,
We are using the legacy .NET Framework 4.6.1 (MVC 5/Angular) version 6.1.1 with Single Deployment/Multiple Database
For our POST action in our AccountController for Login we want to currently limit authentication of users who have the role 'Administrator'.
Following the call to GetLoginResultAsync we have added this code:
var isAdministrator = loginResult.Identity.Claims
.Where(x => x.Type == ClaimTypes.Role)
.Any(x => x.Value == Constants.Administrator);
if (!isAdministrator)
return Json(new AjaxResponse { Success = false, UnAuthorizedRequest = true, Error = new ErrorInfo(L("InvalidRole")) });
This works, however we noticed our Javascript side localization was missing. After investigating we discovered the call to http://localhost:5000/AbpScripts/GetScripts was failing with this exception:
WARN 2021-01-13 11:37:58,042 [12 ] Abp.Logging.LogHelper - Abp.Authorization.AbpAuthorizationException: Current user did not login to the application!
at Abp.Authorization.AuthorizationHelper.Authorize(IEnumerable`1 authorizeAttributes) at Abp.Web.Mvc.Authorization.AbpMvcAuthorizeFilter.OnAuthorization(AuthorizationContext filterContext) Abp.Authorization.AbpAuthorizationException: Current user did not login to the application! at Abp.Authorization.AuthorizationHelper.Authorize(IEnumerable`1 authorizeAttributes)
at Abp.Web.Mvc.Authorization.AbpMvcAuthorizeFilter.OnAuthorization(AuthorizationContext filterContext)
ERROR 2021-01-13 11:38:46,234 [15 ] Web.Mvc.Controllers.AbpScriptsController - There is no role with name: Administrator
Abp.AbpException: There is no role with name: Administrator
at Abp.Authorization.Roles.AbpRoleManager\`2.d\_\_59.MoveNext()
\-\-\- End of stack trace from previous location where exception was thrown \-\-\-
In our AbpRoleManager class we overrode the GetRoleByNameAsync method with the following:
public override async Task<Role> GetRoleByNameAsync(string roleName)
{
return await Task.FromResult(_roleRepository
.GetAll()
.SingleOrDefault(x => String.Equals(x.Name, roleName)));
}
All this works but we wanted to touch base and figure out why this was necessary.
Thanks!
Hi Zony,
Thank you very much for your reply. This will work for some use cases, but not the one we have. We have a situation where multiple settings could be added and/or updated for a certain Tenant. Is there any API or process to reload all of the settings? Thank you again.
We use 9.3 MVC/Jquery with .net framework
We understand that data in the abpSettings table is loaded at start-up. Is there a way to programmatically reload the settings form this table? We have a use case where a setting would change and we don't want the whole site to go down so that setting can be reloaded / refreshed.
Thank you for your time and effort.
Thank you for your response. Since we are using the "L()" method, it appears that I would have to create my own
LocalizationSource.GetString(name, args)
this is in LocalizationSourceExtensions.cs. Do I create another version of LocalizationSourceExtensions as well? or can I create that in my own NullLocalizationSource.cs? Or is this a better way?
Thanks again for your help.
Hello Volosoft,
We have the need to have localization of error messages. For example, "TenantId is missing" or "ConnectionString is missing."
To accomplish this we have added this to AbpZeroTemplate.xml:
<text name="CustomNotFound">{0} is missing</text>
That works great.
Then our code will return such errors like:
var errormessage = L("CustomNotFound", nameof(tenantId));
or
var errormessage = L("CustomNotFound", nameof(connectionId));
This also works great.
Then we go to test it. So we have a test to look for "TenantId is missing" or "ConnectionString is missing" like this
[Fact]
public async Task AddCoursesAsync_NullTenantId_ReturnsFailure()
{
const string ErrorMessage = "tenantId is missing.";
var tenantId = null;
var actual = await _sut.Validate(tenantId, _connectionString);
actual.Error.ShouldBe(ErrorMessage);
}
[Fact]
public async Task AddCoursesAsync_NullConnectionString_ReturnsFailure()
{
const string ErrorMessage = "connectionString is missing.";
var connectionString = null;
var actual = await _sut.Validate(_tenantId, connectionString);
actual.Error.ShouldBe(ErrorMessage);
}
Note that we are using:
_sut.LocalizationManager = NullLocalizationManager.Instance;
per this post: https://support.aspnetzero.com/QA/Questions/1836
So it will return the error of "CustomNotFound." In this case, we need it to be unique. It cannot be "CustomNotFound" for both cases. Is it possible to get back something like "CustomNotFound, tenantId" or something like that to make it unique? In this module alone we have 114 unit tests based on this. We would like to avoid creating a localization for each one.
Update after response from @ismcagdas:
Thank you for your response. Since we are using the "L()" method, it appears that I would have to create my own
LocalizationSource.GetString(name, args)
this is in LocalizationSourceExtensions.cs. Do I create another version of LocalizationSourceExtensions as well? or can I create that in my own NullLocalizationSource.cs? Or is this a better way?
Thanks again for your help.
We worked around the issue by extracting functionality into two separate hub classes and registered them independently.
We changed our IOC registrations in AbpZeroTemplateWebModule in PreInitialize from:
Old Way
IocManager.IocContainer.Register(
Component.For<IMessage<string>, IStatusMessage>()
.ImplementedBy<ManagementHub>()
.LifestyleSingleton()
);
New Way
IocManager.Register<IMessage, ManagementHub>();
IocManager.Register<IStatusMessage, AgentHub>();
The AgentHub's OnConnected and OnDisconnected is not firing, however the ManagementHub now is. This works for our needs.
Dear Volosoft Representative,
Steps to reproduce:
namespace MyCompanyName.AbpZeroTemplate.Web.Hubs
{
using Abp.Dependency;
using Abp.Runtime.Session;
using Castle.Core.Logging;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System.Threading.Tasks;
[HubName("management")]
public class ManagementHub: Hub, ITransientDependency
{
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; }
public ManagementHub()
{
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
}
public override Task OnConnected()
{
await base.OnConnected();
Logger.Debug($"A client connected to ManagementHub: {Context.ConnectionId}.");
return Task.FromResult(0);
}
public override Task OnDisconnected(bool stopCalled)
{
await base.OnDisconnected(stopCalled);
Logger.Debug($"A client disconnected to ManagementHub: {Context.ConnectionId}.");
return Task.FromResult(0);
}
}
}
using System.Web.Mvc;
namespace MyCompanyName.AbpZeroTemplate.Web.Areas.Mpa.Controllers
{
public class TestController : Controller
{
// GET: Mpa/Test
public ActionResult Index()
{
return View("Index");
}
}
}
@{
ViewBag.Title = "title";
}
<h2>Welcome to TEST</h2>