配置如何解析ClientContext

本文关键字:ClientContext 何解析 配置 | 更新日期: 2023-09-27 18:18:15

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

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

因此,为了能够在ASP. js中使用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();
    }

注意,SharePointContextFilterAttribute, SharePointContextProvider, SharePointContext是在从"Apps for SharePoint"项目模板创建项目时自动生成的

配置如何解析ClientContext

因此,您的控制器依赖于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>();

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

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
});

你可以像这样传递一个ClientContext的实例给构造函数:

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