-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create Abp Module without dependencies on main app #1476
Comments
Hi, Actually, depending and sharing on a common database was harder since it requires dbcontext inheritance. I created blog module like that since I think in a real big application, you will have a common/shared base code and plugins/modules depends on that common code. Anyway, I think it should be easier to create such a completely independent module. I would want to create one but I'm very busy on angular2 integration nowadays. Have you tried it? What difficulties you have? Maybe I can help you on your specific problems. |
Actually I've did that.
I've used the way that's used in the blog sample module but I had a tough time with database migration |
@hikalkan The main problem and the first one is: Is that possible to inherit from AbpZeroDbContext without have the entities depending on main app ? Or should I go for AbpDbContext inheritance rather than AbpZeroDbContext ? @iyhammad Can you shed some light on your DbContext? Are you inheriting from AbpDbContext and NOT AbpZeroDbContext ? If so, How do you managed to use the [AutoRepositoryTypes] ? I mean, are you able to use IMyCustomModuleRepository ? |
@iyhammad are you able to share the source code? You can ommit sensitive data of course. |
Hi, |
@iyhammad Thanks for sharing this information. When you say "The default instance did the work for me." what does that mean? |
If you didn't specify [AutoRepositoryTypesAttribute] on your DbContext ABP will Register IRepository for all your DbContext DbSets. |
Thanks @iyhammad I see. I had only tried inheriting from AbpZeroDbContext. I will try with AbpDbContext. In the meantime, do you know if its possible to use multitenancy filters and softdelete without AbpZeroDbContext? Do I need to replicate the code ? |
Which class register IRepository<> ? Its not working for me. It never gets registered Which module should I depend upon ? |
You should depend on AbpZeroEntityFramework |
I always recommend you to create a new empty Abp solution and have a look at it. |
@iyhammad The problem relies on remove dependencies from main app If I derive from AbpZero the module will need the main app database in order to work propertly. I want to avoid that. Microservices should not have dependencies (not even in runtime) Thats the purpose of my question (this issue) I need a way to use it without dependency on main app database (using inheritance) |
The usual solution works fine (they get injected correctly) but Im trying to setup a completely independent module as per my first post. |
I think if we split AbpZeroDbContext without any dbsets to a super class (abstract) we can inherit from it without getting dependencies on roles/users/tenants etc @hikalkan is that feasible ? Would that inject correct repositories ? I will check abpzero source code in order to see if I can find a solution for this particular case. Anyway this is just the initial step for Abp.Soa.Microservice :) |
I read the whole discussion, I'm writing my response as I understand:
|
@hikalkan Thanks for sharing.
@hikalkan I believe you got my goal so far, The issue Im haivng it now is: By Inheriting from AbpDbContext why IRepository and IDbContextProvider are not injected ? I had to remove dependency on AbpZeroCoreModule because it requires classes like Tenant/User/Role (which I dont have for this specific case for example). I already tested to create all classes pretty much like the sample blog module. But at runtime Abp does not manage to insert the correct row at the microservice database. |
I made a mistake...Repositories for my dbcontext (inherited from AbpDbContext) were not being injected because i've missed to add a dependency on my data module to AbpEntityFrameworkModule. Now they are getting injected. Im moving forward with this approach and lets see. I will share my findings when I have this piece working Lets keep this discussion open :) |
I'm also interested in any findings about having many independent/dynamic modules. |
@hikalkan Im having this exception in this microservice trying to use AbpIntegratedTestBase Effort.Exceptions.EffortException : Database has not been initialized If using CodeFirst try to add the following line: Can you please point me on the direction where the Effort Database is initialized ? |
Ok as a workaround I have created the following code at AppTestBase constructor:
|
Im trying to use a DbContext inherited from AbpDbContext but in my tests I got an exception from TenantCache which depends on IRepository that does not exists in my dbcontext. We should not add DbSet into microservices. This is coupling. I believe this happens because there are some REAL validation against an existing tenant. Which should be configurable at least. If I add IDbSet then the excpetion comes from validation:
What about creating a configuration to disable tenant existence validation ? |
I narrowed down to |
@brunobertechini can you share stack trace so we may see dependencies. Also, I suggest an idea: Let build such a module together as open source. You code your simplified requirements, I review code, comment and contribute. |
@hikalkan Im not using multi-db. Im using only one db (my microservice db) I am already preparing a sample code to share using github. It will be available soon. Just finishing some urgent work :) |
+1 i will be glad for sample code |
Hello Guys, How this story finished? did you moved to another post? there's no more comments here, Thanks and Regards |
Hi all, I created a sample project and will want to write an article on that. Project repository: https://github.com/aspnetboilerplate/modular-todo-app What I did:
Current main application has a direct dependency to TodoModule.Web, but it could be easily added as plugin. I added direct dependency just to make it simpler. Actually it does not depend on any class in the module. Check source code and ask questions if you have. As I said, I'll create an article when I have time for it. |
Is it possibile to do the same with angular implementation? |
@ismcagdas thanks. I'm aware of the table mapping. I've tried to create a User entity in the first project and created Users table in db, then a MyUser entity in the second project which is mapped to the same Users table in the same db, but add-migration tool generated xxxxxxx_migration.cs file still contains code for creating Users table. Is there a way to prevent add-migration from creating the existing tables ? or do I have to to modify the generated migration code by hand ? |
Have you created the table by migration at the first place ? |
Yes, I have run update-database in project 1 first. and Users table is already exist in database. If try to run update-database in project 2, it will fail with error like "There is already an object named 'Users' in the database." @ismcagdas |
@ismcagdas When we have multiple contexts with depending on AbpDbContext at migration time Abp generating basic tables (AbpUser, Settings, AuditedLog,...). I need to remove all duplicate model builder from myNewMigration.cs |
@eureky You can remove duplicate tables from yourMigration.cs in your customModule to running your Db-Update without this error. Then waiting for fix issue. |
@eureky I think I understand your problem now but this is related to EF rather than ABP. So, you can create a 3. DbContext just for managing migrations. Then you can create two interfaces for your DbContests. IDbContext1, IDbContext2 for example and define your DbSets in those interfaces. Probably there will be some problematic cases like when you want to use same entity in both dbContexts. In that case, you can override OnModelCreating and ignore them manually. |
@ismcagdas Yes, I understand it's EF. So can I infer that in the Todo example, the code for creating AbpUser table in todo module was removed manually ? (https://github.com/aspnetboilerplate/modular-todo-app/blob/master/src/todo-module/TodoModule.EntityFramework/Migrations/201702031754143_Initial_Todo_Migration.cs) I will look into and try your solution later. I think it's OK for a module to manage migrations for it's own entities. But the major problem is the entity referenced from other module. Looks like it can only be handled in a manual way. |
@eureky migrations are disabled for Todo Module's DbContext https://github.com/aspnetboilerplate/modular-todo-app/blob/master/src/todo-module/TodoModule.EntityFramework/Migrations/Configuration.cs and TodoUser doesn't add a new field to existing User entity (AbpUsers table). |
@eureky , @ismcagdas , @hitaspdotnet Just read your doubts/comments and here are my two cents: First of all, I believe we are talking about AbpZeroDbContext and not AbpDbContext correct? This Abp problem of AbpZeroDbContext is one (very important) issue that drived me to create this project to accomplish complete separation of concerns. If you inherit from AbpZeroDbContext you are saying your module MUST HAVE all tables implemented by it (users, roles, etc -- provided by module zero). This should be a concern only for your "platform" or main app module. Not for every custom module. Even if you REMOVE the instructions from migration file, the RUNTIME dependency still remains (at runtime it will search and look for tables). My recommendation for now, is dont inherit from AbpZeroDbContext, instead, use AbpDbContext. for a clean environment and add your dbsets. Please let me know if you have any doubts. Bruno |
Updated first post with more TODO items. |
I referred directly to AbpDbContext. Where did you see AbpZeroDbContext in my comment? In fact, you cannot use AbpZeroDbContext without referring to the user, roles and tenant entity. |
@hitaspdotnet AbpDbContext does not contain USers and Roles. So there is no way migrations creating instructions for these tables:
AbpDbContext does not generate that. Bruno |
@hitaspdotnet can you share some code? |
And DbContext
BlogPost and BlogComment entities depended on FullAuditedEntity |
I have ready to run multi-language blog service with all available Abp wonderful feature. |
@hitaspdotnet Regarding your BlogServiceDbContext , is this the only dbcontext in your whole solution ? Is this a separated service? This code should not generate more than 3 tables specified as your DbSets. Please check if this dbcontext is not being added twice alltogether with default main app dbcontext. Are you able to share a repository so I can help you out ? Perhaps this is something related to configuration. I remember I had this issue in the past, but I need to recall from my git history how I did it. If you look at the source code of 3.5.0 AbpDbContext.cs, you can see that there is no DbSet configured, thus, no tables should be generated. using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Abp.Collections.Extensions;
using Abp.Configuration.Startup;
using Abp.Dependency;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using Abp.Domain.Uow;
using Abp.Events.Bus;
using Abp.Events.Bus.Entities;
using Abp.Extensions;
using Abp.Reflection;
using Abp.Runtime.Session;
using Abp.Timing;
using Castle.Core.Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Abp.EntityFrameworkCore
{
/// <summary>
/// Base class for all DbContext classes in the application.
/// </summary>
public abstract class AbpDbContext : DbContext, ITransientDependency
{
/// <summary>
/// Used to get current session values.
/// </summary>
public IAbpSession AbpSession { get; set; }
/// <summary>
/// Used to trigger entity change events.
/// </summary>
public IEntityChangeEventHelper EntityChangeEventHelper { get; set; }
/// <summary>
/// Reference to the logger.
/// </summary>
public ILogger Logger { get; set; }
/// <summary>
/// Reference to the event bus.
/// </summary>
public IEventBus EventBus { get; set; }
/// <summary>
/// Reference to GUID generator.
/// </summary>
public IGuidGenerator GuidGenerator { get; set; }
/// <summary>
/// Reference to the current UOW provider.
/// </summary>
public ICurrentUnitOfWorkProvider CurrentUnitOfWorkProvider { get; set; }
/// <summary>
/// Reference to multi tenancy configuration.
/// </summary>
public IMultiTenancyConfig MultiTenancyConfig { get; set; }
/// <summary>
/// Can be used to suppress automatically setting TenantId on SaveChanges.
/// Default: false.
/// </summary>
public virtual bool SuppressAutoSetTenantId { get; set; }
protected virtual int? CurrentTenantId => GetCurrentTenantIdOrNull();
protected virtual bool IsSoftDeleteFilterEnabled => CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled(AbpDataFilters.SoftDelete) == true;
protected virtual bool IsMayHaveTenantFilterEnabled => CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled(AbpDataFilters.MayHaveTenant) == true;
protected virtual bool IsMustHaveTenantFilterEnabled => CurrentTenantId != null && CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled(AbpDataFilters.MustHaveTenant) == true;
private static MethodInfo ConfigureGlobalFiltersMethodInfo = typeof(AbpDbContext).GetMethod(nameof(ConfigureGlobalFilters), BindingFlags.Instance | BindingFlags.NonPublic);
/// <summary>
/// Constructor.
/// </summary>
protected AbpDbContext(DbContextOptions options)
: base(options)
{
InitializeDbContext();
}
private void InitializeDbContext()
{
SetNullsForInjectedProperties();
}
private void SetNullsForInjectedProperties()
{
Logger = NullLogger.Instance;
AbpSession = NullAbpSession.Instance;
EntityChangeEventHelper = NullEntityChangeEventHelper.Instance;
GuidGenerator = SequentialGuidGenerator.Instance;
EventBus = NullEventBus.Instance;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
ConfigureGlobalFiltersMethodInfo
.MakeGenericMethod(entityType.ClrType)
.Invoke(this, new object[] { modelBuilder, entityType });
}
}
protected void ConfigureGlobalFilters<TEntity>(ModelBuilder modelBuilder, IMutableEntityType entityType)
where TEntity : class
{
if (entityType.BaseType == null && ShouldFilterEntity<TEntity>(entityType))
{
var filterExpression = CreateFilterExpression<TEntity>();
if (filterExpression != null)
{
modelBuilder.Entity<TEntity>().HasQueryFilter(filterExpression);
}
}
}
protected virtual bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType) where TEntity : class
{
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
return true;
}
if (typeof(IMayHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
return true;
}
if (typeof(IMustHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
return true;
}
return false;
}
protected virtual Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
where TEntity : class
{
Expression<Func<TEntity, bool>> expression = null;
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:
* !IsSoftDeleteFilterEnabled || !((ISoftDelete) e).IsDeleted
* But this causes a problem with EF Core (see https://github.com/aspnet/EntityFrameworkCore/issues/9502)
* So, we made a workaround to make it working. It works same as above.
*/
Expression<Func<TEntity, bool>> softDeleteFilter = e => !((ISoftDelete)e).IsDeleted || ((ISoftDelete)e).IsDeleted != IsSoftDeleteFilterEnabled;
expression = expression == null ? softDeleteFilter : CombineExpressions(expression, softDeleteFilter);
}
if (typeof(IMayHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:
* !IsMayHaveTenantFilterEnabled || ((IMayHaveTenant)e).TenantId == CurrentTenantId
* But this causes a problem with EF Core (see https://github.com/aspnet/EntityFrameworkCore/issues/9502)
* So, we made a workaround to make it working. It works same as above.
*/
Expression<Func<TEntity, bool>> mayHaveTenantFilter = e => ((IMayHaveTenant)e).TenantId == CurrentTenantId || (((IMayHaveTenant)e).TenantId == CurrentTenantId) == IsMayHaveTenantFilterEnabled;
expression = expression == null ? mayHaveTenantFilter : CombineExpressions(expression, mayHaveTenantFilter);
}
if (typeof(IMustHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:
* !IsMustHaveTenantFilterEnabled || ((IMustHaveTenant)e).TenantId == CurrentTenantId
* But this causes a problem with EF Core (see https://github.com/aspnet/EntityFrameworkCore/issues/9502)
* So, we made a workaround to make it working. It works same as above.
*/
Expression<Func<TEntity, bool>> mustHaveTenantFilter = e => ((IMustHaveTenant)e).TenantId == CurrentTenantId || (((IMustHaveTenant)e).TenantId == CurrentTenantId) == IsMustHaveTenantFilterEnabled;
expression = expression == null ? mustHaveTenantFilter : CombineExpressions(expression, mustHaveTenantFilter);
}
return expression;
}
public override int SaveChanges()
{
try
{
var changeReport = ApplyAbpConcepts();
var result = base.SaveChanges();
EntityChangeEventHelper.TriggerEvents(changeReport);
return result;
}
catch (DbUpdateConcurrencyException ex)
{
throw new AbpDbConcurrencyException(ex.Message, ex);
}
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
try
{
var changeReport = ApplyAbpConcepts();
var result = await base.SaveChangesAsync(cancellationToken);
await EntityChangeEventHelper.TriggerEventsAsync(changeReport);
return result;
}
catch (DbUpdateConcurrencyException ex)
{
throw new AbpDbConcurrencyException(ex.Message, ex);
}
}
protected virtual EntityChangeReport ApplyAbpConcepts()
{
var changeReport = new EntityChangeReport();
var userId = GetAuditUserId();
foreach (var entry in ChangeTracker.Entries().ToList())
{
ApplyAbpConcepts(entry, userId, changeReport);
}
return changeReport;
}
protected virtual void ApplyAbpConcepts(EntityEntry entry, long? userId, EntityChangeReport changeReport)
{
switch (entry.State)
{
case EntityState.Added:
ApplyAbpConceptsForAddedEntity(entry, userId, changeReport);
break;
case EntityState.Modified:
ApplyAbpConceptsForModifiedEntity(entry, userId, changeReport);
break;
case EntityState.Deleted:
ApplyAbpConceptsForDeletedEntity(entry, userId, changeReport);
break;
}
AddDomainEvents(changeReport.DomainEvents, entry.Entity);
}
protected virtual void ApplyAbpConceptsForAddedEntity(EntityEntry entry, long? userId, EntityChangeReport changeReport)
{
CheckAndSetId(entry);
CheckAndSetMustHaveTenantIdProperty(entry.Entity);
CheckAndSetMayHaveTenantIdProperty(entry.Entity);
SetCreationAuditProperties(entry.Entity, userId);
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Created));
}
protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, long? userId, EntityChangeReport changeReport)
{
SetModificationAuditProperties(entry.Entity, userId);
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{
SetDeletionAuditProperties(entry.Entity, userId);
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
}
else
{
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Updated));
}
}
protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, long? userId, EntityChangeReport changeReport)
{
CancelDeletionForSoftDelete(entry);
SetDeletionAuditProperties(entry.Entity, userId);
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
}
protected virtual void AddDomainEvents(List<DomainEventEntry> domainEvents, object entityAsObj)
{
var generatesDomainEventsEntity = entityAsObj as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null)
{
return;
}
if (generatesDomainEventsEntity.DomainEvents.IsNullOrEmpty())
{
return;
}
domainEvents.AddRange(generatesDomainEventsEntity.DomainEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData)));
generatesDomainEventsEntity.DomainEvents.Clear();
}
protected virtual void CheckAndSetId(EntityEntry entry)
{
//Set GUID Ids
var entity = entry.Entity as IEntity<Guid>;
if (entity != null && entity.Id == Guid.Empty)
{
var dbGeneratedAttr = ReflectionHelper
.GetSingleAttributeOrDefault<DatabaseGeneratedAttribute>(
entry.Property("Id").Metadata.PropertyInfo
);
if (dbGeneratedAttr == null || dbGeneratedAttr.DatabaseGeneratedOption == DatabaseGeneratedOption.None)
{
entity.Id = GuidGenerator.Create();
}
}
}
protected virtual void CheckAndSetMustHaveTenantIdProperty(object entityAsObj)
{
if (SuppressAutoSetTenantId)
{
return;
}
//Only set IMustHaveTenant entities
if (!(entityAsObj is IMustHaveTenant))
{
return;
}
var entity = entityAsObj.As<IMustHaveTenant>();
//Don't set if it's already set
if (entity.TenantId != 0)
{
return;
}
var currentTenantId = GetCurrentTenantIdOrNull();
if (currentTenantId != null)
{
entity.TenantId = currentTenantId.Value;
}
else
{
throw new AbpException("Can not set TenantId to 0 for IMustHaveTenant entities!");
}
}
protected virtual void CheckAndSetMayHaveTenantIdProperty(object entityAsObj)
{
if (SuppressAutoSetTenantId)
{
return;
}
//Only works for single tenant applications
if (MultiTenancyConfig?.IsEnabled ?? false)
{
return;
}
//Only set IMayHaveTenant entities
if (!(entityAsObj is IMayHaveTenant))
{
return;
}
var entity = entityAsObj.As<IMayHaveTenant>();
//Don't set if it's already set
if (entity.TenantId != null)
{
return;
}
entity.TenantId = GetCurrentTenantIdOrNull();
}
protected virtual void SetCreationAuditProperties(object entityAsObj, long? userId)
{
EntityAuditingHelper.SetCreationAuditProperties(MultiTenancyConfig, entityAsObj, AbpSession.TenantId, userId);
}
protected virtual void SetModificationAuditProperties(object entityAsObj, long? userId)
{
EntityAuditingHelper.SetModificationAuditProperties(MultiTenancyConfig, entityAsObj, AbpSession.TenantId, userId);
}
protected virtual void CancelDeletionForSoftDelete(EntityEntry entry)
{
if (!(entry.Entity is ISoftDelete))
{
return;
}
entry.Reload();
entry.State = EntityState.Modified;
entry.Entity.As<ISoftDelete>().IsDeleted = true;
}
protected virtual void SetDeletionAuditProperties(object entityAsObj, long? userId)
{
if (entityAsObj is IHasDeletionTime)
{
var entity = entityAsObj.As<IHasDeletionTime>();
if (entity.DeletionTime == null)
{
entity.DeletionTime = Clock.Now;
}
}
if (entityAsObj is IDeletionAudited)
{
var entity = entityAsObj.As<IDeletionAudited>();
if (entity.DeleterUserId != null)
{
return;
}
if (userId == null)
{
entity.DeleterUserId = null;
return;
}
//Special check for multi-tenant entities
if (entity is IMayHaveTenant || entity is IMustHaveTenant)
{
//Sets LastModifierUserId only if current user is in same tenant/host with the given entity
if ((entity is IMayHaveTenant && entity.As<IMayHaveTenant>().TenantId == AbpSession.TenantId) ||
(entity is IMustHaveTenant && entity.As<IMustHaveTenant>().TenantId == AbpSession.TenantId))
{
entity.DeleterUserId = userId;
}
else
{
entity.DeleterUserId = null;
}
}
else
{
entity.DeleterUserId = userId;
}
}
}
protected virtual long? GetAuditUserId()
{
if (AbpSession.UserId.HasValue &&
CurrentUnitOfWorkProvider != null &&
CurrentUnitOfWorkProvider.Current != null &&
CurrentUnitOfWorkProvider.Current.GetTenantId() == AbpSession.TenantId)
{
return AbpSession.UserId;
}
return null;
}
protected virtual int? GetCurrentTenantIdOrNull()
{
if (CurrentUnitOfWorkProvider != null &&
CurrentUnitOfWorkProvider.Current != null)
{
return CurrentUnitOfWorkProvider.Current.GetTenantId();
}
return AbpSession.TenantId;
}
protected virtual Expression<Func<T, bool>> CombineExpressions<T>(Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
var parameter = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expression1.Parameters[0], parameter);
var left = leftVisitor.Visit(expression1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expression2.Parameters[0], parameter);
var right = rightVisitor.Visit(expression2.Body);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
}
class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
{
return _newValue;
}
return base.Visit(node);
}
}
}
} |
@brunobertechini I cant share repo because it's ASPNET Zero based solution.
Yes, Just a reference to [Web.MVC] for depending nothing else.
In the end, thank you for sharing ALL LINES of AbpDbContext!!! |
I reviewed your project completely. I researched about micro-services for two months. I tell you directly that Angular based micro-service is very complicated, to the extent that the Microsoft team that produced the eShopOnContiners sample code has dropped out of its training and provided a brief explanation for the UI based on Angular uses MVC for Identity calls. So in ABP you haven't just Username & Password checker (as eShopOnContiner), you have tenants, roles, features, localization, edition, ... Regards |
Abp architecture for MicroServices/SOA
Goal: Create a working sample using Abp in order to illustrate MicroServices/SOA approach.
The concept I am willing to achieve is SOA Done Right, or Microservices, or BoundedContexts if you will. They all refer to the same concept like One Service (separated from main app) should be autonomous and do not depend on anything else.
In this concept of SOA services, whithin each service you are free to use DDD, CQRS, any database you want. For now, I am using Abp/DDD approach for the Blog Module because it is the first one being developed for this sample and will adhere better with Abp for our discussions.
Sample: https://github.com/brunobertechini/abp-microservices
Items that need interaction/issues to be opened in AspnetBoilerplate:
Pending: Add the full list of requirements to this post.
I will update this list whenever I have a time-frame available.
P.s.: I have edited this HEAD post in order to have a on-going task list for this issue.
------ Original Post ---------
Hello Halil,
I would like to manage to create a complete separate module using Abp / ModuleZero in order to have the nice features like multtenancy and so on.
I can't see a way to do that with current infrastructure provided by Abp. I would like to know if you have any tips for me so I can create a fork of Abp/Abp.Zero and make the necessary changes to do that.
Related to #88 (see my comments)
Lets state the main priorities
If you just point me to the direction you have in mind I can work it out.
Bruno
The text was updated successfully, but these errors were encountered: