7

解决了 !

非常感谢 Sam Leach

这是我的工作 app.config 文件的示例:

<configuration>
       ...
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-2.5.19.0" newVersion="2.5.19.0"/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="System.Net.Http" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-2.1.10.0" newVersion="2.1.10.0"/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-2.1.10.0" newVersion="2.1.10.0"/>
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>

我也找到了那个来源


编辑:原始问题在该行下方。

我正在使用 .NET 4.0 Framework,根据我的研究,我知道不再需要 System.Threading.Tasks 程序集(因为它是自动包含的)。我错了吗?

如果我是对的,我现在很确定该错误已引发,因为 google-api-dotnet-client 的开发人员使用的 System.Threading.Tasks 版本与 Visual Studio 2010 使用的版本不同。

我注意到当我删除一些行时检查我的应用程序的行为时。

这些行出来了:

gcal = new CalendarService(new BaseClientService.Initializer()
{
    Authenticator = auth,
    ApplicationName = APP_NAME,
});

所以,我的新问题是:

有没有办法强制在 VS2010 中使用一个特定版本的参考程序集?

感谢您帮助我,我相信它会帮助很多人,因为 google-calendar-api-v3 的文档记录很差。

亲切的问候,布鲁诺。


我的问题是我无法通过 VisualStudio 访问 Google API 作为服务。

我收到此错误:

L'exception System.IO.FileLoadException n'a pas été gérée
  Message=Impossible de charger le fichier ou l'assembly 'System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' ou une de ses dépendances. La définition trouvée du manifeste de l'assembly ne correspond pas à la référence de l'assembly. (Exception de HRESULT : 0x80131040)
  Source=MVMA
  FileName=System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  FusionLog==== Informations d'état de liaison préalable ===
JRN : utilisateur = MODAL\brbo
JRN : DisplayName = System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 (Fully-specified)
JRN : Appbase = file:///C:/Users/brbo/Documents/Visual Studio 2010/Projects/MVMA-V5.0 (With Gantt)/MVMA/bin/Debug/
JRN : PrivatePath initial = NULL
Assembly appelant : Google.Apis, Version=1.4.0.28227, Culture=neutral, PublicKeyToken=null.
===
JRN : cette liaison démarre dans le contexte de chargement de default.
JRN : utilisation du fichier de configuration de l'application : C:\Users\brbo\Documents\Visual Studio 2010\Projects\MVMA-V5.0 (With Gantt)\MVMA\bin\Debug\MVMA.vshost.exe.Config
JRN : utilisation du fichier de configuration d'hôte : 
JRN : utilisation du fichier de configuration de l'ordinateur à partir de C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
JRN : référence post-stratégie : System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
JRN : tentative de téléchargement de la nouvelle URL file:///C:/Users/brbo/Documents/Visual Studio 2010/Projects/MVMA-V5.0 (With Gantt)/MVMA/bin/Debug/System.Threading.Tasks.DLL.
AVT : la comparaison du nom de l'assembly a entraîné l'incompatibilité : Version principale
ERR : impossible de terminer l'installation de l'assembly (hr = 0x80131040). Détection terminée.

  StackTrace:
   à MVMA.Classes.GoogleCalendar.BuildCalendarService()
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

这是我的类(相同的命名空间),它尝试使用 Json Web 令牌连接到 Google Calendar API v3:

public class GoogleCalendar
{

    // Chaînes d'accès aux services Google
    public const string APP_NAME = "HIDDEN";
    public const string CLIENT_ID = "HIDDEN";
    public const string CLIENT_SECRET = "HIDDEN";
    public const string SERVICE_CLIENT_ID = "HIDDEN";
    public const string EMAIL_ADDRESS = "HIDDEN";
    public const string PUB_KEY = "HIDDEN";
    public const string PRIV_KEY_PATH = @"C:\MVMA\HIDDEN-privatekey.p12";
    public const string PRIV_KEY_SECRET = "notasecret";
    public const string SIMPLE_API_KEY = "HIDDEN";
    public const string SCOPE_CALENDAR = "https://www.googleapis.com/auth/calendar";
    public const string SCOPE_CALENDAR_READONLY = "https://www.googleapis.com/auth/calendar.readonly";

    private static CalendarService gcal;

    public static void ImportFromMVMA()
    {
        Thread yat = new Thread(new ThreadStart(BuildCalendarService));
        yat.Start();
    }

    // Permet de récupérer le service calendrier
    // Define the method that receives a callback when the results are available.
    private static void BuildCalendarService() {

        var certificate = new X509Certificate2(PRIV_KEY_PATH, PRIV_KEY_SECRET, X509KeyStorageFlags.Exportable);
        var privateKey = certificate.Export(X509ContentType.Cert);

        var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
        {
            ServiceAccountId = EMAIL_ADDRESS,
            Scope = SCOPE_CALENDAR
        };

        var auth = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);

        gcal = new CalendarService(new BaseClientService.Initializer()
                        {
                            Authenticator = auth,
                            ApplicationName = APP_NAME,
                        });
    }
}

public enum JwtHashAlgorithm
{
    RS256,
    HS384,
    HS512
}

public class JsonWebToken
{


    private static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;

    static JsonWebToken()
    {
        HashAlgorithms = new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>>
        {
            { JwtHashAlgorithm.RS256, (key, value) => { using (var sha = new HMACSHA256(key)) { return sha.ComputeHash(value); } } },
            { JwtHashAlgorithm.HS384, (key, value) => { using (var sha = new HMACSHA384(key)) { return sha.ComputeHash(value); } } },
            { JwtHashAlgorithm.HS512, (key, value) => { using (var sha = new HMACSHA512(key)) { return sha.ComputeHash(value); } } }
        };
    }

    public static string Encode(object payload, string key, JwtHashAlgorithm algorithm)
    {
        return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm);
    }

    public static string Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm)
    {
        var segments = new List<string>();
        var header = new { alg = algorithm.ToString(), typ = "JWT" };

        byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
        byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));

        segments.Add(Base64UrlEncode(headerBytes));
        segments.Add(Base64UrlEncode(payloadBytes));

        var stringToSign = string.Join(".", segments.ToArray());

        var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

        byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign);
        segments.Add(Base64UrlEncode(signature));

        return string.Join(".", segments.ToArray());
    }

    public static string Decode(string token, string key)
    {
        return Decode(token, key, true);
    }

    public static string Decode(string token, string key, bool verify)
    {
        var parts = token.Split('.');
        var header = parts[0];
        var payload = parts[1];
        byte[] crypto = Base64UrlDecode(parts[2]);

        var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
        var headerData = JObject.Parse(headerJson);
        var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
        var payloadData = JObject.Parse(payloadJson);

        if (verify)
        {
            var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
            var keyBytes = Encoding.UTF8.GetBytes(key);
            var algorithm = (string)headerData["alg"];

            var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
            var decodedCrypto = Convert.ToBase64String(crypto);
            var decodedSignature = Convert.ToBase64String(signature);

            if (decodedCrypto != decodedSignature)
            {
                throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
            }
        }

        return payloadData.ToString();
    }

    private static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
    {
        switch (algorithm)
        {
            case "RS256": return JwtHashAlgorithm.RS256;
            case "HS384": return JwtHashAlgorithm.HS384;
            case "HS512": return JwtHashAlgorithm.HS512;
            default: throw new InvalidOperationException("Algorithm not supported.");
        }
    }

    // from JWT spec
    private static string Base64UrlEncode(byte[] input)
    {
        var output = Convert.ToBase64String(input);
        output = output.Split('=')[0]; // Remove any trailing '='s
        output = output.Replace('+', '-'); // 62nd char of encoding
        output = output.Replace('/', '_'); // 63rd char of encoding
        return output;
    }

    // from JWT spec
    private static byte[] Base64UrlDecode(string input)
    {
        var output = input;
        output = output.Replace('-', '+'); // 62nd char of encoding
        output = output.Replace('_', '/'); // 63rd char of encoding
        switch (output.Length % 4) // Pad with trailing '='s
        {
            case 0: break; // No pad chars in this case
            case 2: output += "=="; break; // Two pad chars
            case 3: output += "="; break; // One pad char
            default: throw new System.Exception("Illegal base64url string!");
        }
        var converted = Convert.FromBase64String(output); // Standard base64 decoder
        return converted;
    }
}

public class GoogleJsonWebToken
{

    public static string GetAccessToken(string email, string certificateFilePath, string serviceScope)
    {
        var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        var issueTime = DateTime.Now;

        var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
        var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds; // Expiration time is up to 1 hour, but lets play on safe side

        var payload = new
        {
            iss = email,
            scope = serviceScope,
            aud = "https://accounts.google.com/o/oauth2/token",
            exp = exp,
            iat = iat
        };

        var certificate = new X509Certificate2(certificateFilePath, GoogleCalendar.PRIV_KEY_SECRET);

        var privateKey = certificate.Export(X509ContentType.Cert);

        return JsonWebToken.Encode(payload, privateKey, JwtHashAlgorithm.RS256);
    }
}

我不明白为什么 System.Threading.Tasks 程序集上有 FileLoadException。我尝试向已经使用 Tasks 的应用程序添加新功能而没有问题。这个应用程序使用在不同线程中运行的 TabPage 对象。

4

1 回答 1

6

删除所有引用System.Threading.Tasks,然后从您使用的任何 .NET 版本(.NET 4.0)中添加一个。

Google Calendar API 可能正在使用不同版本的 .NET

手动重定向程序集

您可以在 app.config 中指定要使用的程序集版本

  <dependentAssembly>
    <assemblyIdentity name="someAssembly"
      publicKeyToken="32ab4ba45e0a69a1"
      culture="en-us" />

    <bindingRedirect oldVersion="7.0.0.0" newVersion="8.0.0.0" />
  </dependentAssembly>
于 2013-07-10T14:49:20.177 回答