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 591 posts 2346 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 388 posts 1360 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 591 posts 2346 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 591 posts 2346 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);
            }
    
        }
    
Please Sign in or register to post replies

Write your reply to:

Draft