Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Ulrik Nedergaard 44 posts 175 karma points
    Apr 06, 2020 @ 13:24
    Ulrik Nedergaard
    0

    Create nodes in a loop in custom class ( Hangfire ) v8.6

    Hi folks!

    I am trying to figure what goes wrong in my code, or approach.

    The short story is that I have a list of texts, and for each text I am checking if a node under a certain parentnode ( id 1077 in this case ) exists. If it doesn't I'll create a node.

    This is setup in a Hangfire job and actually kinda works.

    If it finds all the nodes it runs without problems, but if it need to create a node, it might run for the first 2, 3 or sometimes maybe 5 creations and then stop. Hangfire wil run again ( 10 retries ) and each time get the next bunch of nodes and maybe eventually having added all the nodes, but that is not ideal.

    foreach (string txt in myListOfStrings)
            {
                IContentService contentService = Current.Services.ContentService;
    
                long p = 1;
    
                var nodeExists = contentService.GetPagedChildren(1077, 0, 10000, out p).Where(x => x.Name.ToString() == txt).Any();
    
                if (!nodeExists)
                {
                    var newNode = contentService.Create(txt, 1077, "color");
                    contentService.SaveAndPublish(newNode);
                }
            }
    

    It returns a lot of exception code in hangfire. First line often being : System.NullReferenceException: Object reference not set to an instance of an object.

  • Yakov Lebski 594 posts 2350 karma points
    Apr 07, 2020 @ 12:17
    Yakov Lebski
    0

    I think what handfire run without umbraco context. In this case Current is null Current.Services.ContentService; Try inject IContentService to hangfire job

  • Marcio Goularte 389 posts 1362 karma points
    Apr 07, 2020 @ 13:53
  • Ulrik Nedergaard 44 posts 175 karma points
    Apr 07, 2020 @ 18:48
    Ulrik Nedergaard
    0

    Thanks for your replies Marcio and Yakov

    I guess that might be the way to do it. I'm just really unsure how it's done. Could you guide me?

    I have made this test which works. It gets the node and changes the "Name". So I guess the contentservice is not NULL :) However - cs.SaveAndPublish(someNode) will not work

    public class Jobs
    {
        public void RunJobs()
        {
            RecurringJob.AddOrUpdate(() => Test(null), "0 0 31 2 0");
        }
    
    
        public void Test(PerformContext hangfire)
        {
            hangfire.WriteLine("STARTING TEST");
    
            IContentService cs = Umbraco.Core.Composing.Current.Services.ContentService;
    
            var someNode = cs.GetById(1077);
            someNode.Name = "TEST RENAME OF NODE";
            cs.Save(someNode);
    
    
        }
    
  • Yakov Lebski 594 posts 2350 karma points
    Apr 07, 2020 @ 21:04
    Yakov Lebski
    0

    I Job constructor you should add IContentService contentService parameter and init local variable _contentService

    inside Test you should use _contentService

  • Ulrik Nedergaard 44 posts 175 karma points
    Apr 08, 2020 @ 09:13
    Ulrik Nedergaard
    0

    Is it done like this ?

    public class Jobs
    {
    
        private IContentService _contentService;
    
        public Jobs(IContentService contentService)
        {
            _contentService = contentService;
        }
    
        public void RunJobs()
        {
            RecurringJob.AddOrUpdate(() => Test(null), "0 0 31 2 0");
        }
    
    
        public void Test(PerformContext hangfire)
        {
            hangfire.WriteLine("STARTING TEST");
    
            var someNode = _contentService.GetById(1077);
            someNode.Name = "TEST RENAME OF NODE";
            _contentService.Save(someNode);
    
    
        }
    }
    

    This gives me this error

    System.MissingMethodException No parameterless constructor defined for this object. System.MissingMethodException: No parameterless constructor defined for this object. at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) at System.Activator.CreateInstance(Type type, Boolean nonPublic) at System.Activator.CreateInstance(Type type) at Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(Type type) at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context) at Hangfire.Server.BackgroundJobPerformer.<>cDisplayClass9_0.0() at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func1 continuation) at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func1 continuation) at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable`1 filters) at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context) at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext context, IStorageConnection connection, String jobId)

  • Yakov Lebski 594 posts 2350 karma points
    Apr 08, 2020 @ 14:13
    Yakov Lebski
    0

    Looks like you job in not passed via DI, you can see here good example how to define it

    https://our.umbraco.com/forum/umbraco-8/96445-hangfire-dependency-injection

  • Ulrik Nedergaard 44 posts 175 karma points
    Apr 09, 2020 @ 22:24
    Ulrik Nedergaard
    0

    1 days effords to make it work later. I'm not sure if I'm doing it right. Injection is something I never have done before, and I'm really not sure if I'm on the right track here. Here are my files ( or at least parts of them ) :

    public class UmbracoStandardOwinStartup : UmbracoDefaultOwinStartup
    {
    
    
    
        public override void Configuration(IAppBuilder app)
        {
            //ensure the default options are configured
            base.Configuration(app);
    
            // Configure hangfire
            var options = new SqlServerStorageOptions { PrepareSchemaIfNecessary = true };
            const string umbracoConnectionName = Umbraco.Core.Constants.System.UmbracoConnectionName;
            var connectionString = System.Configuration
                .ConfigurationManager
                .ConnectionStrings[umbracoConnectionName]
                .ConnectionString;
    
            GlobalConfiguration.Configuration
                .UseSqlServerStorage(connectionString, options)
                .UseConsole();
    
            // Give hangfire a URL and start the server                
            var dashboardOptions = new DashboardOptions { Authorization = new[] { new UmbracoAuthorizationFilter() } };
            app.UseHangfireDashboard("/hangfire", dashboardOptions);
            app.UseHangfireServer();
    
    
            RecurringJob.AddOrUpdate<Jobs>(x => x.Test2(null), "0 0 31 2 0");
    
    }
    

    -

    public class SiteService : ISiteService
    {
        public string CreateNode(string name)
        {
            var umbf = Umbraco.Web.Composing.Current.Factory.GetInstance<IUmbracoContextFactory>();
            using (var contextf = umbf.EnsureUmbracoContext())
            {
                var umbcontext = contextf.UmbracoContext;
    
                IContentService cs = Umbraco.Core.Composing.Current.Services.ContentService;
    
                var newNode = cs.Create(name, 1077, "color");
                newNode.SetValue("colorName", name + " " + name);
                var res = cs.SaveAndPublish(newNode).Result.ToString();
                return name + " // " + res.ToString();
            }    
        }
    }
    
    public interface ISiteService
    {
        string CreateNode(string name);
    }
    

    -

    public class Jobs
    {
    
    
        private ISiteService _siteService;
    
        public Jobs()
        {
            _siteService = new SiteService();
        }
    
        public void Test2(PerformContext hangfire)
        {
    
    
            for (int i = 0; i < 20; i++)
            {
                hangfire.WriteLine(_siteService.CreateNode("Tester " + i));
            }
        }
    }
    

    Running the Test2 job 3 times gave these results

    1. run : All good - 20 nodes created. Succes!

    2. run : 12 nodes created and then this error : System.ArgumentException: An item with the same key has already been added.

    3. run : 14 nodes created and then this error : System.NullReferenceException: Object reference not set to an instance of an object.

    So - sometimes it works and sometimes it doesn't. Same as before.

    If I do like this in the Jobs class

    public class Jobs
    {
        private ISiteService _siteService;
    
        public Jobs(ISiteService siteService)
        {
            _siteService = siteService;
        }
    

    ... I just get this error immediately

    System.MissingMethodException: No parameterless constructor defined for this object.

  • Ulrik Nedergaard 44 posts 175 karma points
    Apr 10, 2020 @ 09:29
    Ulrik Nedergaard
    0

    Came across this post on faking httpcontext, and making a httpRequest to some site, seems to be making all my problems go away - even without using DI

    https://our.umbraco.com/forum/extending-umbraco-and-using-the-api/76889-background-process-value-cannot-be-nullparameter-name-httpcontext

    Not the prettiest solution but it works.

    Maybe there is a better way? Maybe with DI?

    public void Test(PerformContext hangfire)
        {
    
            HttpContext.Current = new HttpContext(new HttpRequest(null, "https://www.google.com", null), new HttpResponse(null));
    
            var umbf = Umbraco.Web.Composing.Current.Factory.GetInstance<IUmbracoContextFactory>();
            using (var cref = umbf.EnsureUmbracoContext())
            {
                var cache = cref.UmbracoContext.ContentCache;
    
                hangfire.WriteLine("STARTING TEST");
                IContentService contentService = Current.Services.ContentService;
                var someNode = contentService.GetById(1077);
    
    
                hangfire.WriteLine(someNode.Name);
                someNode.Name = "TEST RENAME OF NODE";
                contentService.SaveAndPublish(someNode);
            }
    
        }
    
  • This forum is in read-only mode while we transition to the new forum.

    You can continue this topic on the new forum by tapping the "Continue discussion" link below.

Please Sign in or register to post replies