scheduled task using JobBase

Vote:
 

Hi,

We are using System.Web.Security.Membership.GetUser(); in a Scheduled Job task but since inheriting JobBase to get the Progress of the Job the Membership is returning Null - without inheriting JobBase it works well.
PS we are not using MVC.
Thanks
Jon

#81109
Feb 10, 2014 14:49
Vote:
 

Is this happening when the job is run automatically or manually?

Frederik

#81114
Feb 10, 2014 16:30
Vote:
 

Manually

#81118
Feb 10, 2014 17:41
Vote:
 

This is interesting. Can you check what identity you have when inheriting from JobBase and when not?

HttpContext.Current.User.Identity.Name
Thread.CurrentPrincipal.Identity.Name

    

Also which membership providers are you using (paste fragment from web.config)?

#81137
Feb 11, 2014 8:16
Vote:
 

Hi, this is the fragments from the webconfig.

 <roleManager enabled="true" defaultProvider="MultiplexingRoleProvider" cacheRolesInCookie="true">
      <providers>
        <clear/>
        <add name="MultiplexingRoleProvider" type="EPiServer.Security.MultiplexingRoleProvider, EPiServer.Framework" provider1="SqlServerRoleProvider" provider2="WindowsRoleProvider" providerMap1="SqlServerMembershipProvider" providerMap2="WindowsMembershipProvider"/>
        <add name="WindowsRoleProvider" applicationName="EPiServerSample" type="EPiServer.Security.WindowsRoleProvider, EPiServer"/>
        <add name="SqlServerRoleProvider" connectionStringName="EPiServerDB" applicationName="EPiServerSample" type="System.Web.Security.SqlRoleProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
        <add name="EPiServerCommonRoleProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.RoleProvider, EPiServer.Common.Web.Authorization"/>
        <!-- ECF Start -->
        <add connectionStringName="EcfSqlConnection" applicationName="rcog-shop-uat" name="CMSRoleProvider" type="Mediachase.Commerce.Customers.Profile.Providers.CustomerSqlRoleProvider, Mediachase.Commerce"/>
        <!-- ECF End -->
      </providers>
    </roleManager>
    <membership defaultProvider="MultiplexingMembershipProvider" userIsOnlineTimeWindow="10" hashAlgorithmType="HMACSHA512">
      <providers>
        <clear/>
        <add name="MultiplexingMembershipProvider" type="EPiServer.Security.MultiplexingMembershipProvider, EPiServer.Framework" provider1="SqlServerMembershipProvider" provider2="WindowsMembershipProvider"/>
        <add name="WindowsMembershipProvider" type="EPiServer.Security.WindowsMembershipProvider, EPiServer" deletePrefix="BUILTIN\" searchByEmail="true"/>
        <add name="SqlServerMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="EPiServerDB" requiresQuestionAndAnswer="false" applicationName="EPiServerSample" requiresUniqueEmail="true" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>
        <add name="EPiServerCommonMembershipProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.MembershipProvider, EPiServer.Common.Web.Authorization"/>
        <!-- ECF Start -->
        <add connectionStringName="EcfSqlConnection" applicationName="rcog-shop-uat" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed" passwordStrengthRegularExpression="" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" name="CMSMembershipProvider" type="Mediachase.Commerce.Customers.Profile.Providers.CustomerSqlMembershipProvider, Mediachase.Commerce"/>
        <!-- ECF End -->
      </providers>
    </membership>

    I'll find out the identity when I can  debug the code.

Thanks for your help

#81147
Feb 11, 2014 9:13
Vote:
 

If the job is run automaticly the HttpContext.Current is null. And the Thread.CurrentPrincipal.Identity.Name should be the service account. Don't think this have anything to do with JobBase. Ths is working as intended :(

 
#81150
Feb 11, 2014 9:19
Vote:
 

Hi,

Thanks. What Im doing is a manual Job that imports users from an external source. As each user comes into the system i am setting up Membership roles etc. Is this something that is possible using the JobBase?

 

Thanks

#81155
Feb 11, 2014 9:23
Vote:
 

Hi Anders,

When you say that this is working as intended do you mean that we have a fault with our code and that the Membership should work as it does without the JobBase or do you mean that the Membership should NOT work when we Inherit JobBase?

 

#81156
Feb 11, 2014 9:36
Vote:
 

As I rember the JobBase creates a thread, and executes the the thread. It will start to construct a object from your class. That should be done in the current thread.. So try to make a reference to your user in the constructor.

 

#81158
Feb 11, 2014 9:37
Vote:
 

the code that is run in ScheduledJob is

private void InternalExec(bool startThread)
{
	try
	{
		Type type = Assembly.Load(this.AssemblyName).GetType(this.TypeName);
		if (type.IsSubclassOf(typeof(JobBase)))
		{
			lock (ScheduledJob._syncRoot)
			{
				if (ScheduledJob._runningJobs.ContainsKey(this.ID))
				{
					return;
				}
			}
			Thread thread = new Thread(delegate
			{
				Timer isAliveTimer = null;
				JobBase jobBase = null;
				try
				{
					jobBase = (JobBase)Activator.CreateInstance(type);
					jobBase.ScheduledJobId = this.ID;
					lock (ScheduledJob._syncRoot)
					{
						ScheduledJob._runningJobs.Add(this.ID, jobBase);
					}
					jobBase.StatusChanged += new EventHandler<JobStatusChangedEventArgs>(this.job_StatusChanged);
					this._internalRunning = true;
					this.PingDatabaseWithRunningState(this.ID);
					isAliveTimer = new Timer(new TimerCallback(this.PingDatabaseWithRunningState), jobBase.ScheduledJobId, this._pingTime, this._pingTime);
					string text = jobBase.Execute();
					ScheduledJob.DataAccess.Service().ReportExecuteItem(jobBase.ScheduledJobId, 0, text);
				}
				catch (Exception exception2)
				{
					ScheduledJob.log.Error(string.Format("Job {0} failed", this.TypeName), exception2);
					ScheduledJob.ReportError(this.ID, exception2);
				}
				finally
				{
					lock (ScheduledJob._syncRoot)
					{
						ScheduledJob._runningJobs.Remove(this.ID);
					}
					if (jobBase != null)
					{
						jobBase.StatusChanged -= new EventHandler<JobStatusChangedEventArgs>(this.job_StatusChanged);
					}
					this._internalRunning = false;
					isAliveTimer = this.CleanupTimer(isAliveTimer);
					ScheduledJob.DataAccess.Service().UpdateRunningState(this.ID, this._internalRunning);
				}
			});
			thread.Start();
		}
		else
		{
			if (startThread)
			{
				Thread thread2 = new Thread(delegate
				{
					this.ExecuteStaticMethod(type);
				});
				thread2.Start();
			}
			else
			{
				this.ExecuteStaticMethod(type);
			}
		}
	}
	catch (FileNotFoundException exception)
	{
		ScheduledJob.log.Error(string.Format("Failed to load assembly '{0}' to remove this job permanent remove the contents of tblScheduledItemLog and tblScheduledItem for the job with jobId ='{1}' from the database", this.AssemblyName, this.ID), exception);
		ScheduledJob.ReportError(this.ID, exception);
	}
	catch (Exception ex)
	{
		ScheduledJob.log.Error(string.Format("3.1.2 Failed to execute job {0}", this.ID), (ex.InnerException != null) ? ex.InnerException : ex);
		ScheduledJob.ReportError(this.ID, ex);
	}
}

    

#81159
Feb 11, 2014 9:39
Vote:
 

Some more details on your use case would be great - "... As each user comes into the system i am setting up Membership roles etc. Is this something that is possible using the JobBase?".

Scheduled jobs behave differently around current user identity when executed manually or automatically - but I'm not quite sure that you need current user.

#81161
Feb 11, 2014 9:52
Vote:
 

If he has a job that only should be done manually I can se the reason for needing to log who startet the job.....

#81162
Feb 11, 2014 9:54
Vote:
 

Anders: As of 7.5 the job runs inside of the app pool (scheduler service removed). So I would assume that when running the job automatically the HttpContext.Current is not null and the current user is the app pool user.

#81164
Feb 11, 2014 10:00
Vote:
 

Mari: The same code is run as far as I can tell. So it's inside a thread, and I think the HttpContext.Current is nulll there

#81165
Feb 11, 2014 10:04
Vote:
 

This is a callstack before your job's Execute method is called:

>	YourJob.Import.Execute() Line 46	C#
 	EPiServer.dll!EPiServer.DataAbstraction.ScheduledJob.InternalExec.AnonymousMethod__5()	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.Execute()	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot)	Unknown
 	mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution)	Unknown
 	mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()	Unknown
 	[Native to Managed Transition]	

    

HttpContext is not surviving thread. It just goes on holidays then..

#81167
Feb 11, 2014 10:10
Vote:
 
But the easy soulution is to make a reference to it in the constructor....


public class Test:JobBase { string UserName=""; public Test() { UserName=HttpContext.Current.User.Identity.Name; } }

    

#81168
Edited, Feb 11, 2014 10:12
Vote:
 

What would you do with UserName then?

#81169
Feb 11, 2014 10:13
Vote:
 
#81170
Feb 11, 2014 10:14
Vote:
 

My point is that you can make a reference to the System.Web.Security.Membership there instead of the name of the current user

#81172
Feb 11, 2014 10:21
Vote:
 

How do you make a reference to System.Web.Security.Membership in a constructor?

#81173
Feb 11, 2014 10:23
Vote:
 

Andres: as type construction happens already in Thread start lambda - HttpContext is null there as well.

#81174
Feb 11, 2014 10:23
Vote:
 

HI, Thanks for all your imput - so, to summarize, is this possible to do or not?

Thanks again

#81180
Feb 11, 2014 10:38
Vote:
 

Unfortunately with all the side discussions I'm lost with proper task you need to accomplish :) Do you need to create a user during manual job execution, do you need to get access to current user executing job manually, or something else?

#81181
Feb 11, 2014 11:01
Vote:
 

:)

What we have written is a Scheduled job that imports users from an external source. As we loop through each record we create / update a Commerce User and create/update a CMS User (member) which sets up their Roles etc. This worked well but as there are over 4,000 users we wanted to include JobBase to write a Progress message on screen together with a Stop button. But since Inheriting JobBase the Membership Create User / GetUser doesnt work any more -  it is throwing an Object Reference is not set to an Instance of an object Error.

#81182
Feb 11, 2014 11:08
Vote:
 

Can you post exact stack trace and failing location? Wondering what and where excatly is failing. Difference between inheriting from JobBase and not is just telling EPiServer to monitor StatusChanged event..

#81186
Feb 11, 2014 11:31
Vote:
 

Hi, thanks for your help.

It fails on the CreateUser / getUser but we will have to leave this for now as we have run out of time.

Thanks again

#81190
Feb 11, 2014 11:47