Hello,
I am trying to write unit tests in a situation where we have multiple tenants and each tenant has its own database. Not matter what is tried, this error keeps getting thrown: Castle.MicroKernel.ComponentActivator.ComponentActivatorException ComponentActivator: could not instantiate We start the test like this:
namespace MyCompanyName.AbpZeroTemplate.Tests.Migration
{
using AbpZeroTemplate.Migration;
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;
public class MigrationAppService_Tests : AppTestBase
{
private readonly IMigrationAppService _migrationAppService;
public MigrationAppService_Tests()
{
LoginAsHostAdmin();
_migrationAppService = Resolve<IMigrationAppService>();
}
[Fact]
public async Task IfTenantIsMissingSaveZipAsyncThrowsException()
{
await _migrationAppService.SaveZipAsync(1, new MemoryStream(), String.Empty);
}
}
}
The "stystem under test" is a service here:
namespace MyCompanyName.AbpZeroTemplate.Migration
{
using Abp.Domain.Repositories;
using MultiTenancy;
using System;
using System.IO;
using System.Threading.Tasks;
public class MigrationAppService : AbpZeroTemplateAppServiceBase, IMigrationAppService
{
private readonly IRepository<Tenant> _tenantRepository;
public MigrationAppService(IRepository<Tenant> tenantRepository)
{
_tenantRepository = tenantRepository;
}
public async Task SaveZipAsync(int id, Stream stream, string path)
{
var tenant = await _tenantRepository.GetAsync(id);
if (tenant == null)
throw new Exception($"Tenant with {id} was not found.");
}
}
}
The tenant and host DB context is attached. It appears that the error surfaces in AbpZeroTemplateTenantDbContext.AbpZeroTemplateTenantDbContext() and might be trying to use the tenant context instead of the host. The 15 lines of the error is:
Castle.MicroKernel.ComponentActivator.ComponentActivatorException
ComponentActivator: could not instantiate MyCompanyName.AbpZeroTemplate.EntityFramework.AbpZeroTemplateTenantDbContext
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstanceCore(ConstructorCandidate constructor, Object[] arguments, Type implType)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateInstance(CreationContext context, ConstructorCandidate constructor, Object[] arguments)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context, Burden burden)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.CreateInstance(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
at Abp.EntityFramework.DefaultDbContextResolver.Resolve[TDbContext](String connectionString) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.EntityFramework\EntityFramework\DefaultDbContextResolver.cs:line 30
at Abp.EntityFramework.Uow.EfUnitOfWork.GetOrCreateDbContext[TDbContext](Nullable`1 multiTenancySide) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.EntityFramework\EntityFramework\Uow\EfUnitOfWork.cs:line 130
at Castle.Proxies.EfRepositoryBase`3Proxy_14.get_Context_callback()
Can you advise on how to write this simple unit test or how to avoid this error?
Thank you for your help. DBContexts.zip
Hello,
Thank you for your response. We do not use the ChatMessage and Friendship tables. They are not referenced anywhere in our code. We do not get this error until we include the javascript files in our layout. When we drop the tables and try to add a migration, it does not find find these tables to add. To use signalR in abpZero template, are these tables required? Is there some sort of dependenct injection requirement? We can manually create a migration instead of adding the table manually through SQL. Is that what you mean?
For more background, our Tenent DB Context File looks like this:
namespace MyCompanyName.AbpZeroTemplate.EntityFramework
{
// using statments;
public class AbpZeroTemplateTenantDbContext : AbpZeroTenantDbContext<Role, User>
{
public virtual IDbSet<Department> Departments { get; set; }
public virtual IDbSet<Enrollment> Enrollments { get; set; }
public AbpZeroTemplateTenantDbContext() : base("Tenant")
{
}
public AbpZeroTemplateTenantDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
/* This constructor is used in tests to pass a fake/mock connection.
*/
public AbpZeroTemplateTenantDbContext(DbConnection dbConnection)
: base(dbConnection, true)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new DepartmentConfiguration());
modelBuilder.Configurations.Add(new EnrollmentConfiguration());
}
}
}
and the error we are getting is attached.
Hello,
We recently tried to implement signalR change notifications (following [http://www.aspnetboilerplate.com/Pages/Documents/SignalR-Integration]) )with host and tenant dbContexts. When we ran, there were errors: UserFriendsCache: {"Invalid object name 'dbo.AppFriendships'."} and also the same error for table AppChatMessages. A screenshot of this error is attached. I apologize if file is blurry, but it would not let me upload a larger file. If it is hard to read, tell me and I can upload multiple images of error.
When we manually create those tables in the tenant DB, then the error goes away. Is this the correct fix?
<ins>Background Information:</ins> in our layout page:
<script src="~/wwwroot/js/jquery.signalR-2.2.1.js" type="text/javascript"></script>
<script src="~/signalr/hubs" type="text/javascript"></script>
// ..a few other script files
<script src="~/wwwroot/js/abp.signalr.js" type="text/javascript"></script>
The ChangeNotificationHub.cs file looks like this:
namespace MyCompanyName.AbpZeroTemplate.Web.Hubs
{
using Abp.Dependency;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System.Threading.Tasks;
[HubName("changeNotify")]
public class ChangeNotificationHub: Hub, ITransientDependency
{
public Task Deregister(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
public Task Register(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public void Save(string groupName)
{
Clients.OthersInGroup(groupName).changePending();
}
}
}
and the changeNotification.js file looks like this:
(function () {
var changeNotifyHub = $.connection.changeNotify;
// Connected
abp.event.on('abp.signalr.connected', function () {
changeNotifyHub.server.register(ams.getCurrentUrl());
});
// Disconnected
abp.event.on('abp.signalr.disconnected', function () {
changeNotifyHub.server.deregister(ams.getCurrentUrl());
});
changeNotifyHub.client.changePending = function () {
$('#saveAlert').toggleClass('hidden');
// buttonGroup.state = reset;
};
$.connection.hub.start();
})();
Thank you again for your help. Please let me know if you need any more information.
After scouring the Git repository's comment messages, I determined we had some out-of-date NuGet packages. I eventually updated all the packages except for EntityFramework.DynamicFilters. Next, I added the missing Changed_Code_MaxLength_Of_OrganizationUnit migration. Finally, I built and ran and the exception went away. I push the changes the broken branch of the example repo.
I replicated the changes in our real project and after fixing several changes like; Clock.SupportsMultipleTimezone from a method to a property, renaming IdentityFrameworkClaimsAbpSession to ClaimsAbpSession and updating the CustomDtoMapper's CreateMappingsInternal method. I built and ran and the exception is gone.
-Chris
@ismcagdas
Sorry for the tardy reply and the long explanatory post to follow.
To illustrate the problem I created a File->New Project->ASP.NET Web Application (.NET Framework)
Working Branch git clone <a class="postlink" href="http://git.carelearning.com/dxabp.git">http://git.carelearning.com/dxabp.git</a> example -b working
When I build and run the Example project from the working branch; I select click "Upload", a DevExpress FileUploader widget, and navigate to an included sample file located at the [cloned-project-dir]\assets\departments.csv. This in return executes the controller's Upload method shown below.
[HttpPost]
public ActionResult Upload()
{
var file = Request.Files[0];
if (file == null ||
file.ContentLength == 0)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var content = _departmentAppService.Upload(file.InputStream);
return Content(content);
}
Here is the DepartmentAppService Upload method which uses Gembox.Spreadsheet and Json.Net.
public string Upload(Stream stream)
{
SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
var workbook = ExcelFile.Load(stream, LoadOptions.CsvDefault);
var worksheet = workbook.Worksheets.ActiveWorksheet;
// TODO optimize
var dataTable = worksheet.CreateDataTable(new CreateDataTableOptions()
{
ColumnHeaders = true
});
return JsonConvert.SerializeObject(dataTable, new DataSetConverter());
}
When I breakpoint and debug I see the expected Json content string.
Broken Branch git clone <a class="postlink" href="http://git.carelearning.com/dxabp.git">http://git.carelearning.com/dxabp.git</a> example -b broken
To prevent licensing issues when sharing this example code; yesterday I created a brand new Asp.Net Boilerplate project. I created a Custom controller with single Index view. I then transplanted the code from the Working project, slightly altering the code to use the idomatic Abp code and the suggested inheritance hierarchy. I also had to disable the CSRF/XSS protection according to this post.
After building and setting Web as the Startup Project I run update-database -verbose -projectname EntityFramework to bootstrap the required database. The project uses (localdb) for the data source in the connection string.
When running and selecting the same test file at[cloned-project-dir]\assets\departments.csv. The Upload method now throws the aforementioned ReadTimeout not supported exception (see attached screenshot, ReadTimeout_Exception.png).
When searching the forums another user experienced this issue with Kendo Upload Widget. We tried the presented solution of adding MethodInvocationValidator.IgnoredTypesForRecursiveValidation.AddIfNotContains(typeof(HttpPostedFileWrapper)); in the Global.aspx code-behind in our real AbpZero project and it did not work. I also tried adding this code to this example project, however it would not compile. I left it in but commented out.
Thank you for your time and an outstanding library.
I am trying to stream a file to a service class (import). In this simplified example I am getting an error:
public class DepartmentController : AmsControllerBase
{
private readonly IDepartmentAppService _departmentAppService;
public DepartmentController(IDepartmentAppService departmentAppService)
{
_departmentAppService = departmentAppService;
}
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Import()
{
var data = System.IO.File.ReadAllBytes(@"c:\temp\departments.csv");
var stream = new MemoryStream(data);
_departmentAppService.Import(stream);//<--Exception throws here and method is not executed
return null;
}
}
Controller base class is:
public abstract class AmsControllerBase : AbpController
{
protected AmsControllerBase()
{
LocalizationSourceName = AmsConsts.LocalizationSourceName;
}
protected void CheckErrors(IdentityResult identityResult)
{
identityResult.CheckErrors(LocalizationManager);
}
}
The error I get is:
Timeouts are not supported on this stream
Is there a different way I should be passing a stream around? The file's size is about 1 kb. Thanks again for your time and effort.
Thank you for your reply. Here are the steps we used to resolve the issue we had:
Thank you again for your help and effort.
Hello,
We have been trying to set up an application using ASP.NET Zero Template where all users log on at 1 location. Then each tenant (more than 1 user per tenant) has their own database. We can log in as the global admin (no tenant ID) with no errors. However, when we try to login as a tenant user, we get:
Invalid object name 'dbo.AbpUsers'.
This table is only in the Host DB. The Tenant DB does not have this information. When we copy over this table from Host DB to Tenant DB then then we get more errors for multiple ABP* Tables. We want to authenticate against the Host DB, but not have all the abp tables duplicated in the tenant. Is this the correct approach?
For more information, here are the two Context classes:
public class TenantDbContext : AbpDbContext
{
public virtual IDbSet<Department> Departments { get; set; }
public TenantDbContext() :
base("Tenant")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new DepartmentConfiguration());
}
}
public class HostDbContext : AbpZeroHostDbContext<Tenant, Role, User>
{
public virtual IDbSet<BinaryObject> BinaryObjects { get; set; }
public virtual IDbSet<Friendship> Friendships { get; set; }
public virtual IDbSet<ChatMessage> ChatMessages { get; set; }
public HostDbContext()
: base("Host")
{
}
public HostDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public HostDbContext(DbConnection dbConnection)
: base(dbConnection, true)
{
}
}
Thank you for your time and effort.
Thank you. The project is intentionally only Asp.Net Boilerplate so as not to make anything public. I am sorry for not specifying that earlier. Thanks again for all your hard work.
Thank you for your reply. We could not recreate the problem you described above in an empty MVC5/WebAPI project with only the DevExteme widgets added. To be clear, we are not directly using
abp.services.app.department.delete(1);
to invoke our DepartmentAppService. Instead we created a wrapper DepartmentApiController that proxies the requests to the DepartmentAppService because it appears that DevExpress' dxDataGrid.DataSource requires it.
Upon further investigation we discovered an issue using mediaformatters. We found a possible solution by adding FormUrlEncodedMediaTypeFormatter and JQueryMvcFormUrlEncodedFormatter in the PostInitialize Method of the DxAbpWebApiModule class.
To illustrate this case, we have a simple project using: Asp.Net Boilerplate project with DevExpress dxDataGrid and dxButtons. You can find it here: git clone [http://git.carelearning.com/dxabp.git])
You can see the issue if you comment out this method:
public override void PostInitialize()
{
base.PostInitialize();
Configuration.Modules.AbpWebApi().HttpConfiguration.Formatters.Add(new FormUrlEncodedMediaTypeFormatter());
Configuration.Modules.AbpWebApi().HttpConfiguration.Formatters.Add(new JQueryMvcFormUrlEncodedFormatter());
}
And then try to delete a department:
Is this approach advisable? Do you know of a better approach? We see that at aspnetboilerplate/src/Abp.Web.Api/WebApi/AbpWebApiModule.cs you are clearing all formatters except JSON. Therefore we are not sure if adding formatters will cause other problems.