Base solution for your next web application

Activities of "amayorquin"

Hi,

Can you help me please, I'mt trying to use hangfire to set a recurring job to run over all tenants. The job works fine when I use the sample you prepared: <a class="postlink" href="https://gist.github.com/hikalkan/d543b6ded179d1f05227f49bc70e2fbe">https://gist.github.com/hikalkan/d543b6 ... 9bc70e2fbe</a>

But when I tried to use UOW I,m getting an error. This is my code:

In Startup.cs in configuration method I just added this line at the end:

app.UseHangfireDashboard("/hangfire", new DashboardOptions());

In the web module I added this in PostInitialize Methdod

var jobs = IocManager.Resolve<BackgroundJobs>();
RecurringJob.AddOrUpdate("ReplyUpdates", () => jobs.ReplyUpdates(), Cron.MinuteInterval(5));

And my BackgroundJobs class:

public class BackgroundJobs : ITransientDependency
    {
        private readonly ILogger _logger;
        private readonly IRepository<Tenant> _tenantRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;

        public BackgroundJobs(ILogger logger,
            IRepository<Tenant> tenantRepository,
            IUnitOfWorkManager unitOfWorkManager)
        {
            _logger = logger;
            _tenantRepository = tenantRepository;
            _unitOfWorkManager = unitOfWorkManager;
        }

        [UnitOfWork]
        public virtual void ReplyUpdates()
        {
            var tenants = _tenantRepository.GetAllList(t => t.ConnectionString != null && t.ConnectionString != "");
            for (int i = 0; i < tenants.Count; i++)
            {
                var tenantId = tenants[i].Id;
                using (_unitOfWorkManager.Current.SetTenantId(tenantId))
                {
                    _logger.Info("MyJob1Class is working!");
                }
            }
            
        }
    }

When I added the [UnitOfWork] attributte I'm getting the following error, in the hangfire dashboard:

Could not load file or assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

If I not use the [UnitOfWork] attribute the Job runs fine, but _unitOfWorkManager.Current is null

Can you help me please

Thanks in advance

Hi,

I'm using the OData sample to create a new Web API application using ABP. I just made some changes.

In the AbpODataDemoWebApiModule.cs, I changed this:

[DependsOn(typeof(AbpWebApiODataModule))]
    public class AbpODataDemoWebApiModule : AbpModule
    {
        public override void PreInitialize()
        {
            var builder = Configuration.Modules.AbpWebApiOData().ODataModelBuilder;

            //Configure your entities here...
            builder.EntitySet<Person>("Persons");

            //TODO: Remove after ABP v0.11.2.0 upgrade
            Configuration.Validation.IgnoredTypes.AddIfNotContains(typeof(Type));
            Configuration.Validation.IgnoredTypes.AddIfNotContains(typeof(Delta));
        }

        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
        }
    }

for this:

[DependsOn(typeof(AbpWebApiModule))]
    public class AbpODataDemoWebApiModule : AbpModule
    {
        public override void PreInitialize()
        {
         Configuration.Modules.AbpWebApi().HttpConfiguration.MapHttpAttributeRoutes();
        }

        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
        }
    }

In the AbpODataDemoWebModule.cs, I just register the Routes, and I created a new class for the routes:

public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());

            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
public static class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
                );
        }
    }

and finally in the controller changed like this:

public class PersonsController : AbpApiController
    {
        private readonly IRepository<Phone> _phoneRepository;
        private readonly IRepository<Person> _personRepository;

        public PersonsController(
            IRepository<Person> personRepository,
            IRepository<Phone> phoneRepository)
  
        {
            _personRepository = personRepository;
            _phoneRepository = phoneRepository;
        }

        [Route("api/Persons/GetPersons")]
        [HttpGet]
        public List<Person> GetPersons()
        {
            var persons = _personRepository.GetAll().ToList();
            return persons;
        }
}

But when I call the method via Postman I'm getting an exception:

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection

Here is the complete exception:

{ "message": "An error has occurred.", "exceptionMessage": "The 'ObjectContent1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.", "exceptionType": "System.InvalidOperationException", "stackTrace": null, "innerException": { "message": "An error has occurred.", "exceptionMessage": "Error getting value from 'Phones' on 'System.Data.Entity.DynamicProxies.Person_E0D0F5809A10CCF521F5B0560F9B6295EB6C1DAE95890460F8EC9161EADC2F00'.", "exceptionType": "Newtonsoft.Json.JsonSerializationException", "stackTrace": " at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()", "innerException": { "message": "An error has occurred.", "exceptionMessage": "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.", "exceptionType": "System.ObjectDisposedException", "stackTrace": " at System.Data.Entity.Core.Objects.ObjectContext.get_Connection()\r\n at System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption)\r\n at System.Data.Entity.Core.Objects.ObjectQuery1.Execute(MergeOption mergeOption)\r\n at System.Data.Entity.Core.Objects.DataClasses.EntityCollection1.Load(List1 collection, MergeOption mergeOption)\r\n at System.Data.Entity.Core.Objects.DataClasses.EntityCollection1.Load(MergeOption mergeOption)\r\n at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.DeferredLoad()\r\n at System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.LoadProperty[TItem](TItem propertyValue, String relationshipName, String targetRoleName, Boolean mustBeNull, Object wrapperObject)\r\n at System.Data.Entity.Core.Objects.Internal.LazyLoadBehavior.<>c__DisplayClass72.<GetInterceptorDelegate>b__1(TProxy proxy, TItem item)\r\n at System.Data.Entity.DynamicProxies.Person_E0D0F5809A10CCF521F5B0560F9B6295EB6C1DAE95890460F8EC9161EADC2F00.get_Phones()\r\n at GetPhones(Object )\r\n at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)" } } }

Hi, thank you for you response.

I just added a new dummy method in the api AccountController:

[Route("api/Account/Register")]
        [HttpPost]
        public AjaxResponse Register()
        {
            return new AjaxResponse(true);
        }

and just added the route in the Authenticate action:

[Route("api/Account/Authenticate")]

The Authenticate action is working, but the when I hit to Register action is calling the Authenticate action, even if I just call the api like this: <a class="postlink" href="http://localhost:6634/api/Account">http://localhost:6634/api/Account</a> is responding the Authenticate method

Those are the only changes I made. I don't know if I have to configurate something else.

Thanks in advance

Hi,

I'm trying to add a new method to the AccountController in the API project but is not working, looks like the controller just allow one method, I created a different controller and is the same thing. If I rename the Authenticate method to anything else I am still can access the Authenticate method in postman, seems that is the default route and just allow one method. When I added a new method just throw an error but nothing is registered in the error log, can you help me, please. I don't know if I missing something. Thanks in advance

Thank you very much for your responses. Finally I changed the way I call the stored procedure and works now.

I changed to this:

var  layers = Context.Database.SqlQuery<Layer>("GetPersonLayersBySecurityGroups @PersonId",
                new SqlParameter("@PersonId", personId)).ToList();

Hi,

Can you help me, please, I created a Custom Repository to call a stored procedure, and the call to the custom repository is working fine, but I'm getting an error when I test calling the API.

This is my code

Interface in Core project

public interface ILayerAppService : IApplicationService
    {
        Task<ListResultDto<LayerListDto>> GetPersonLayers();
    }

Repository in

public class LayerRepository : CorgeeZeroRepositoryBase<Layer>, ILayerRepository
    {
        public LayerRepository(IDbContextProvider<CorgeeZeroDbContext> dbContextProvider)
        : base(dbContextProvider)
        {
        }

        public List<Layer> GetPersonLayers(long personId)
        {
           List<Layer> layers = new List<Layer>();

            using (var db = GetDbContext())
            {
                db.Database.Initialize(force: false);

                var cmd = db.Database.Connection.CreateCommand();
                cmd.CommandText = "[dbo].[GetPersonLayers]";
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add(new SqlParameter { ParameterName = "@PersonId", Value = personId, SqlValue = personId,   SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Input });

                try
                {
                    db.Database.Connection.Open();

                    using (var reader = cmd.ExecuteReader())
                    {
                        layers = ((IObjectContextAdapter)db).ObjectContext.Translate<Layer>(reader).ToList();
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    db.Database.Connection.Close();
                }
                return layers;
            }
        }
    }

And the AppService:

public class LayerAppService : CorgeeZeroAppServiceBase, ILayerAppService
    {
        private readonly IRepository<Layer> _layerRepository;
        private readonly ILayerRepository _customLayerRepository;

        public LayerAppService(IRepository<Layer> layerRepository, ILayerRepository customLayerRepository)
        {
            _layerRepository = layerRepository;
            _customLayerRepository = customLayerRepository;
        }

       public async Task<ListResultDto<LayerListDto>> GetPersonLayers()
        {
            var currentUser = await this.GetCurrentUserAsync();
            
            var layers = _customLayerRepository.GetPersonLayers(currentUser.Id);

            var mapping = new ListResultDto<LayerListDto>(layers.MapTo<List<LayerListDto>>());

            return mapping;
        }
    }

I'm getting the results correctly in the mapping var , but for some reason the API is returning an error:

"An internal error occurred during your request!"

Answer

Thank you very much. This is how I was thinking to solve it, but I had the doubt if you did this on purpose. Thanks again

Answer

Thanks for your reply,

Actually I'm not change anything yet in code, I just downloaded the ABP and started to check and debug the code.

When I try Login with an external account previously registered (Google), this method is called (tenancyName is empty):

[UnitOfWork]
        public virtual async Task<ActionResult> ExternalLoginCallback(string returnUrl, string tenancyName = "")
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                return RedirectToAction("Login");
            }

            //Try to find tenancy name
            if (tenancyName.IsNullOrEmpty())
            {
                var tenants = await FindPossibleTenantsOfUserAsync(loginInfo.Login);
                switch (tenants.Count)
                {
                    case 0:
                        return await RegisterView(loginInfo);
                    case 1:
                        tenancyName = tenants[0].TenancyName;
                        break;
                    default:
                        return View("TenantSelection", new TenantSelectionViewModel
                        {
                            Action = Url.Action("ExternalLoginCallback", "Account", new { returnUrl }),
                            Tenants = tenants.MapTo<List<TenantSelectionViewModel.TenantInfo>>()
                        });
                }
            }

            var loginResult = await _logInManager.LoginAsync(loginInfo.Login, tenancyName);

            switch (loginResult.Result)
            {
                case AbpLoginResultType.Success:
                    await SignInAsync(loginResult.User, loginResult.Identity, false);

                    if (string.IsNullOrWhiteSpace(returnUrl))
                    {
                        returnUrl = Url.Action("Index", "Home");
                    }

                    return Redirect(returnUrl);
                case AbpLoginResultType.UnknownExternalLogin:
                    return await RegisterView(loginInfo, tenancyName);
                default:
                    throw CreateExceptionForFailedLoginAttempt(loginResult.Result, loginInfo.Email ?? loginInfo.DefaultUserName, tenancyName);
            }
        }

The method that is called to find the possible tenants is the following:

[UnitOfWork]
        protected virtual async Task<List<Tenant>> FindPossibleTenantsOfUserAsync(UserLoginInfo login)
        {
            List<User> allUsers;
            using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
            {
                allUsers = await _userManager.FindAllAsync(login);
            }

            return allUsers
                .Where(u => u.TenantId != null)
                .Select(u => AsyncHelper.RunSync(() => _tenantManager.FindByIdAsync(u.TenantId.Value)))
                .ToList();
        }

but like I said before, looks like is searching only in the host database

Hi, I have the same error in the last version, I'm using different database per tenant. Did you fix it?

Question

Hi,

I have a problem when I use social login to authenticate users (Using different database for each tenant), I can register the user without problems, but when I logout and try to login again, it redirects to the register page. I checked the code and there is a function that try to find possible tenants for that login, but the problem is that is searching in the host database and the user is registered in a tenant database.

I guess I have to pass the tenant name to make this work. Is there a reason you make this in that way? Or is an error?

Thanks in advance!

Showing 1 to 10 of 10 entries