【问题标题】:Configure how ClientContext is resolved配置如何解析 ClientContext
【发布时间】:2015-10-19 12:21:54
【问题描述】:

我正在开展一个项目,我们正在使用存储库模式抽象对 SharePoint 存储数据的访问,如下所示(为简洁起见,省略了详细信息):

    public interface IDocumentRepository { ... }
    public class SharePointDocumentRepository: IDocumentRepository
    {
        public SharePointDocumentRepository(Microsoft.SharePoint.Client.ClientContext clientContext)
        {
            ...
        }
    }

因此,为了能够在 ASP.Net MVC 应用程序中使用 IDocumentRepository 实例,在 UnityConfig.cs 中容器配置如下:

    ...
    container.RegisterType<IDocumentRepository, SharePointDocumentRepository>();
    ...

问题是,在运行时,我得到以下异常:

ClientContext 类型有多个长度为 1 的构造函数。无法 消除歧义。

为了解决这个问题,我需要注册应该如何解析 ClientContext。到目前为止,我尝试了以下方法,但没有成功:

  1. 配置构造函数的使用,有一个字符串参数
  2. 配置 Unity 以在需要 ClientContext 时返回特定实例

所以,我的问题是:有没有办法配置 Unity 来解析 ClientContext,或者我是否应该通过 IDocumentRepository 接口的属性来注入它,只要我得到这个接口的一个实例?注意:第二种方法将使 SharePoint 存储库难以在任何其他组件中使用,这些组件无法访问 HTTP 上下文,因此它不是一个可取的方法。

备注:在不使用 DI 的情况下创建存储库的实例可以如下完成:

    ...
    [SharePointContextFilter]
    public ActionResult Index()
    {
        SharePointContext spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        using(var clientContext = spContext.CreateUserClientContextForSPHost())
        {
            var documentRepository = new SharePointDocumentRepository(clientContext);
            ...
        }
        ...
        return View();
    }

请注意,SharePointContextFilterAttributeSharePointContextProviderSharePointContext 是从“SharePoint 应用程序”项目模板创建项目时自动生成的。

【问题讨论】:

  • 如果你手动创建SharePointDocumentRepositoryClientContext,你会怎么做?您可以将该代码添加到您的问题中吗?

标签: c# asp.net-mvc sharepoint dependency-injection unity-container


【解决方案1】:

所以,你的控制器依赖于IDocumentRepository,但是这种依赖需要基于一些上下文来创建,而这些上下文只能从控制器自身内部获得。对吗?

如果是这种情况,那么您需要使用Factory

在您的情况下,您的工厂应该如下所示:

public interface IDocumentRepositoryFactory
{
    IDocumentRepository Create(Context context);
}

public class SharePointDocumentRepositoryFactory : IDocumentRepositoryFactory
{
    public IDocumentRepository Create(Context context)
    {
        //Create the repository
        SharePointContext spContext = SharePointContextProvider.Current.GetSharePointContext(....);
        using(var clientContext = spContext.CreateUserClientContextForSPHost())
        {
            return new SharePointDocumentRepository(clientContext); 
        }
    }
}

请注意,这里我使用Context 作为Create 方法的输入。您应该将其替换为您需要从控制器中获取的任何上下文,以便能够构建存储库(如果有)。

现在,将控制器的构造函数更改为在构造函数中接受 IDocumentRepositoryFactory 并将其存储在字段中。

别忘了像这样注册工厂:

container.RegisterType<IDocumentRepositoryFactory, SharePointDocumentRepositoryFactory>();

然后在您的操作中,您可以像这样使用工厂创建存储库:

var repository = m_DocumentRepositoryFactory.Create(....);

其中m_DocumentRepositoryFactory 是您用于在构造函数中存储工厂依赖项的字段的名称。

更新:

由于ClientContext 类是一次性的,那么在我们处理掉ClientContext 之后返回SharePointDocumentRepository 是没有意义的。

我建议您改用隔离工厂。像这样:

public interface IDocumentRepositoryIsolationFactory
{
    void CreateAndUse(Context context, Action<IDocumentRepository> action);
}

public class SharePointDocumentRepositoryIsolationFactory : IDocumentRepositoryIsolationFactory
{
    public void CreateAndUse(Context context, Action<IDocumentRepository> action)
    {
        //Create the repository
        SharePointContext spContext = SharePointContextProvider.Current.GetSharePointContext(....);
        using(var clientContext = spContext.CreateUserClientContextForSPHost())
        {
            var repository = SharePointDocumentRepository(clientContext); 

            action(repository);
        }
    }
}

在你的行动中像这样使用它:

m_IsolationFactory.CreateAndUse(... , (repository) =>
{
    //repository is a IDocumentRepository, use it here

});

【讨论】:

  • 第一个提议的解决方案完成了这项工作。不需要隔离工厂,因为上下文依赖于工厂,即它被构造、传递给工厂并由调用者处理。就我而言,这是控制器。
【解决方案2】:

您可以将 ClientContext 的实例传递给构造函数:

var clientContext = new ClientContext();
container.RegisterInstance<ClientContext>("myContext", clientContext);

container.RegisterType<IDocumentRepository, SharePointDocumentRepository>(new InjectionConstructor(container.Resolve<ClientContext>("myContext")));

【讨论】:

  • 这种方法的两个问题:1)ClientContext没有默认构造函数,它的两个构造函数都需要SP站点的URL(见msdn.microsoft.com/en-us/library/office/…),2)我们可以获取URL的唯一地方SP站点的控制器是控制器。是否也可以从那里访问统一容器?
  • 您可能可以通过 HttpContext 访问 SP 站点的 url - (在 UnityConfig 中尚无法访问)。您可以通过在两者之间提供一个工厂来解决此问题,并在可能的情况下接受延迟加载参数来检索 httpcontext。虽然不是最漂亮的方法:/
猜你喜欢
  • 1970-01-01
  • 2020-05-13
  • 2011-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-16
  • 2011-11-25
  • 1970-01-01
相关资源
最近更新 更多