Отладка пользовательского действия SharePoint 2010

Я пытаюсь создать пользовательское действие для SharePoint 2010, которое будет использоваться в рабочем процессе. В конструкторе я вижу действие в раскрывающемся меню «Действие» на ленте в разделе «Пользовательские действия». Однако элемент никогда не материализуется в дизайнере, и я не могу понять, как отладить эту проблему.

Я попытался поместить Debugger.Break() в обработчики событий, но это приводит к записи в журнале событий приложений с надписью An unhandled exception ('Launch for user') occurred in w3wp.exe [xxxx]. Just-In-Time debugging this exception failed with the following error: Not enough storage is available to complete this operation. Кроме того, присоединение отладчика к процессу w3wp.exe не останавливается ни на каких точках останова.

Я включил подробное ведение журнала внутри SharePoint 2010 (и ведение журнала довольно подробное!), но, похоже, оно не содержит никакой информации о компоненте или о том, почему дизайнер не загружает компонент.

Вот соответствующий фрагмент кода для этого очень простого действия. Что я здесь делаю неправильно?

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WorkflowActions;

namespace SharePoint.Activities.IO
{
    public partial class CopyFile : SequenceActivity
    {
        public CopyFile()
        {
            InitializeComponent();
        }

        public static DependencyProperty SourceDirectoryProperty = DependencyProperty.Register("SourceDirectory", typeof(string), typeof(CopyFile));
        public static DependencyProperty TargetDirectoryProperty = DependencyProperty.Register("TargetDirectory", typeof(string), typeof(CopyFile));
        public static DependencyProperty ResultProperty = DependencyProperty.Register("Result", typeof(string), typeof(CopyFile));
        public static DependencyProperty CreateDateProperty = DependencyProperty.Register("CreateDate", typeof(DateTime), typeof(CopyFile));
        public static DependencyProperty CompletionDateProperty = DependencyProperty.Register("CompletionDate", typeof(DateTime), typeof(CopyFile));
        public static DependencyProperty WFContextProperty = DependencyProperty.Register("WFContext", typeof(WorkflowContext), typeof(CopyFile));

        [Description("Path the files to perform the operation on")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string SourceDirectory
        {
            get { return (string)GetValue(SourceDirectoryProperty); }
            set { SetValue(SourceDirectoryProperty, value); }
        }
        [Description("Path the files are copied to")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string TargetDirectory
        {
            get { return (string)GetValue(TargetDirectoryProperty); }
            set { SetValue(TargetDirectoryProperty, value); }
        }
        [Description("Once the the operation completes, this is set to OK or the exception information")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string Result
        {
            get { return (string)GetValue(ResultProperty); }
            set { SetValue(ResultProperty, value); }
        }
        [Description("When the request was created")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public DateTime CreateDate
        {
            get { return (DateTime)GetValue(CreateDateProperty); }
            set { SetValue(CreateDateProperty, value); }
        }
        [Description("When execution stoped")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public DateTime CompletionDateDate
        {
            get { return (DateTime)GetValue(CompletionDateProperty); }
            set { SetValue(CompletionDateProperty, value); }
        }

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public WorkflowContext WFContext { get { return (WorkflowContext)GetValue(WFContextProperty); } set { SetValue(WFContextProperty, value); } }

        protected override void Initialize(IServiceProvider provider)
        {
            TraceIn();
            base.Initialize(provider);
            TraceOut();
        }


        protected override void Uninitialize(IServiceProvider provider)
        {
            TraceIn();
            base.Uninitialize(provider);
            TraceOut();
        }
        public override string ToString()
        {
            TraceIn();
            var result = base.ToString();
            TraceOut();
            return result;
        }
        public override int GetHashCode()
        {
            TraceIn();
            // ReSharper disable BaseObjectGetHashCodeCallInGetHashCode
            var result = base.GetHashCode();
            // ReSharper restore BaseObjectGetHashCodeCallInGetHashCode
            TraceOut();
            return result;
        }
        public override bool Equals(object obj)
        {
            TraceIn();
            // ReSharper disable BaseObjectEqualsIsObjectEquals
            var result = base.Equals(obj);
            // ReSharper restore BaseObjectEqualsIsObjectEquals
            TraceOut();
            return result;
        }
        //This code causes the compiler to blow up! :)
        //protected override void Dispose(bool disposing)
        //{
        //    TraceIn();
        //    base.Dispose(disposing);
        //}
        protected override void SetBoundValue(ActivityBind bind, object value)
        {
            TraceIn();
            base.SetBoundValue(bind, value);
            TraceOut();
        }
        protected override void OnWorkflowChangesCompleted(ActivityExecutionContext executionContext)
        {
            TraceIn();
            base.OnWorkflowChangesCompleted(executionContext);
            TraceOut();
        }
        protected override void OnSequenceComplete(ActivityExecutionContext executionContext)
        {
            TraceIn();
            base.OnSequenceComplete(executionContext);
            TraceOut();
        }
        protected override void OnListChanging(ActivityCollectionChangeEventArgs e)
        {
            TraceIn();
            base.OnListChanging(e);
            TraceOut();
        }
        protected override void OnListChanged(ActivityCollectionChangeEventArgs e)
        {
            TraceIn();
            base.OnListChanged(e);
            TraceOut();
        }
        protected override void OnClosed(IServiceProvider provider)
        {
            TraceIn();
            base.OnClosed(provider);
            TraceOut();
        }
        protected override void OnActivityExecutionContextUnload(IServiceProvider provider)
        {
            TraceIn();
            base.OnActivityExecutionContextUnload(provider);
            TraceOut();
        }
        protected override void OnActivityExecutionContextLoad(IServiceProvider provider)
        {
            TraceIn();
            base.OnActivityExecutionContextLoad(provider);
            TraceOut();
        }
        protected override void OnActivityChangeRemove(ActivityExecutionContext executionContext, Activity removedActivity)
        {
            TraceIn();
            base.OnActivityChangeRemove(executionContext, removedActivity);
            TraceOut();
        }
        protected override void OnActivityChangeAdd(ActivityExecutionContext executionContext, Activity addedActivity)
        {
            TraceIn();
            base.OnActivityChangeAdd(executionContext, addedActivity);
            TraceOut();
        }
        protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
        {
            TraceIn();
            var result = base.HandleFault(executionContext, exception);
            TraceOut();
            return result;
        }
        protected override object GetBoundValue(ActivityBind bind, Type targetType)
        {
            TraceIn();
            var result = base.GetBoundValue(bind, targetType);
            TraceOut();
            return result;
        }
        protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
        {
            TraceIn();
            var result = base.Cancel(executionContext);
            TraceOut();
            return result;
        }
        protected override void InitializeProperties()
        {
            TraceIn();
            base.InitializeProperties();
            TraceOut();
        }
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            TraceIn();

            var isSiteAdmin = false;
            CreateDate = DateTime.Now;
            try
            {
                // Needs administrative credentials to get the web application properties ino
                SPSecurity.RunWithElevatedPrivileges(delegate
                                                         {
                                                             using (var site = new SPSite(WFContext.Site.ID))
                                                             {
                                                                 using (var web = site.AllWebs[WFContext.Web.ID])
                                                                 {
                                                                     isSiteAdmin = web.CurrentUser.IsSiteAdmin;
                                                                     if (string.IsNullOrEmpty(SourceDirectory)) throw new ArgumentException("SourceDirectory cannot be null or empty");
                                                                     if (!Directory.Exists(SourceDirectory)) throw new DirectoryNotFoundException("Could not find source directory: \"" + SourceDirectory + "\"");

                                                                     if (string.IsNullOrEmpty(TargetDirectory)) throw new ArgumentException("TargetDirectory cannot be null or empty");
                                                                     if (!Directory.Exists(TargetDirectory)) throw new DirectoryNotFoundException("Could not find target directory: \"" + TargetDirectory + "\"");
                                                                     // Do something 

                                                                 }
                                                             }
                                                         });

                Result = "OK";
            }
            catch (Exception ex)
            {
                Result = isSiteAdmin ? ex.ToString() : ex.Message;
            }
            CompletionDateDate = DateTime.Now;

            var result = base.Execute(executionContext);
            TraceOut();
            return result;
        }

        private static void TraceIn()
        {
            Trace("Entering");
        }

        private static void TraceOut()
        {
            Trace("Exiting");
        }

        private static void Trace(string direction)
        {
            Debugger.Launch();
            var stackTrace = new StackTrace(2, false);
            var frame = stackTrace.GetFrame(0);
            Debug.WriteLine(direction + " " + frame.GetMethod().Name);
        }
    }
}

Со встроенной трассировкой каждый раз, когда я пытаюсь добавить активность в рабочий процесс в конструкторе, я вижу вызовы GetHashCode() и Equals() и ничего больше.

Вот файл .actions для него.

<?xml version="1.0" encoding="utf-8" ?>
<WorkflowInfo>
    <Actions
        Sequential="then"
        Parallel="and">
        <Action
            Name="IO Activities"
            ClassName="SharePoint.Activities.IO"
            Assembly="SharePoint.Activities.IO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abfb622251cf6982"
            Category="Custom Actions"
            AppliesTo="all">
            <RuleDesigner
                Sentence="CopyFile %1">
                <FieldBind
                    Field="SourceDirectory,TargetDirectory,Result,CreateDate,CompletionDate"
                    Text="Copy the files"
                    DesignerType="CopyFile"
                    Id="1"/>
            </RuleDesigner>
            <Parameters>
                <Parameter
                    Name="SourceDirectory"
                    Type="System.String, mscorlib"
                    Direction="In"
                    DesignerType="StringBuilder"
                    Description="Directory where the source files are to move" />
                <Parameter
                    Name="TargetDirectory"
                    Type="System.String, mscorlib"
                    Direction="In"
                    DesignerType="StringBuilder"
                    Description="Directory where the files will be placed" />
                <Parameter
                    Name="Result"
                    Type="System.String, mscorlib"
                    Direction="Out" />
                <Parameter
                    Name="CreateDate"
                    Type="System.DateTime, mscorlib"
                    Direction="Out" />
                <Parameter
                    Name="CompletionDate"
                    Type="System.DateTime, mscorlib"
                    Direction="Out" />

                <Parameter 
                    Name="WFContext" 
                    Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" 
                    Direction="In" 
                    DesignerType="Hide" />
            </Parameters>
        </Action>
    </Actions>
</WorkflowInfo>

person amber    schedule 07.05.2012    source источник


Ответы (1)


Мы никогда не находим средства для отладки проблемы, и кажется, что SharePoint просто молча терпит неудачу.

Оказывается, причина провала была проста и довольно досадна.

<Action
    Name="IO Activities"
    ClassName="SharePoint.Activities.IO"
    Assembly="SharePoint.Activities.IO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abfb622251cf6982"
    Category="Custom Actions"
    AppliesTo="all">

Интерес представляет ClassName="SharePoint.Activities.IO". Чего не хватает? Фактическое имя класса. Должно читаться ClassName="SharePoint.Activities.IO.FileCopy". После проверки правильности имени класса в проекте в файле web.config и .actions действие теперь загружается и отображается в дизайнере.

person amber    schedule 24.05.2012