配置ClientContext的解析方式

时间:2015-10-19 12:21:54

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

我正在开发一个项目,我们使用存储库模式抽象访问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时返回特定实例
  3. 所以,我的问题是:有没有办法配置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();
        }
    

    注意,从“Apps for SharePoint”项目模板创建项目时,会自动生成SharePointContextFilterAttributeSharePointContextProviderSharePointContext

2 个答案:

答案 0 :(得分:2)

因此,您的控制器依赖于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类是一次性的,因此在我们处理SharePointDocumentRepository之后返回ClientContext没有意义。

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

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

});

答案 1 :(得分:0)

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

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

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