I want to get information from only 1 user out of 20,000 users. The response time of the method I used below is 40 seconds. What is the solution to this problem?
public AuthenticatedUserProperties Info(string Username)
{
try
{
var context = new PrincipalContext(ContextType.Domain, Settings.LDAPDomain, Settings.LDAPContainer, Settings.LDAPUsername, Settings.LDAPPassword);
UserPrincipal user = new UserPrincipal(context);
user.SamAccountName = Username;
var searcher = new PrincipalSearcher(user);
var searchResults = searcher.FindOne();
DirectoryEntry de = searchResults.GetUnderlyingObject() as DirectoryEntry;
ActiveDirectoryUserProperties prop = ConvertLdapUserPropertyToArray(de);
return new AuthenticatedUserProperties
{
Status = true,
Properties = prop
};
}
catch (Exception e)
{
return new AuthenticatedUserProperties
{
Status = false,
Properties = null
};
}
}
I use the System.DirectoryServices.Protocols library instead. It is always blazing fast. I can never get System.DirectoryServices.AccountManagement to have reliable performance and it is often agonizingly slow (10+ seconds) to get just one user. TBH - I think our Network setup is likely to blame causing the bind to be dysfunctional - but the Protocols library yields good results without much effort regardless of our network dysfunction.
You have to do slightly more work - but nothing particularly difficult. I'm not an expert with this library - but this sample code works reliably for me.
using System.DirectoryServices.Protocols;
public class UserInfo
{
public string SAMAccountName;
public string DomainHostName;
public string ADSDirectory;
public Dictionary<string, string> UserAttributes;
// Some attributes not really strings and require extra handling - but simplied for example
// This is really just for illustrative purposes
public UserInfo(string a_SAMAccountName, string a_DomainHostName = "ldap.mydomain:3268", string a_ADSDirectory = "ours.net")
{
UserAttributes = new Dictionary<string, string>();
SAMAccountName = a_SAMAccountName;
DomainHostName = a_DomainHostName;
ADSDirectory = a_ADSDirectory;
}
}
public static class GetUserAttributes
{
public static List<string> WantedAttributes;
static GetUserAttributes()
{
WantedAttributes = new List<string>();
WantedAttributes.Add("mail");
//... Add Properties Wanted
}
public static void GetUserAttributes(UserInfo a_user)
{
using (HostingEnvironment.Impersonate())
{
LdapDirectoryIdentifier z_entry = new LdapDirectoryIdentifier(a_user.DomainHostName, true, false);
using (LdapConnection z_remote = new LdapConnection(z_entry))
{
z_remote.SessionOptions.VerifyServerCertificate = delegate (LdapConnection l, X509Certificate c) { return true; };
z_remote.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
z_remote.SessionOptions.ProtocolVersion = 3;
z_remote.Bind();
SearchRequest z_search = new SearchRequest();
z_search.Scope = System.DirectoryServices.Protocols.SearchScope.Subtree;
z_search.Filter = "(SAMAccountName=" + a_user.SAMAccountName + ")";
z_search.DistinguishedName = a_user.ADSdirectory;
foreach (List<string> z_item in WantedAttributes)
{
z_search.Attributes.Add(z_item);
}
SearchResponse z_response = (SearchResponse)z_remote.SendRequest(z_search);
if (z_response != null)
{
foreach (SearchResultEntry z_result in z_response.Entries)
{
foreach (string z_property in z_result.Attributes.AttributeNames)
{
if (WantedAttributes.ContainsKey(z_property))
{
DirectoryAttribute z_details = a_result.Attributes[z_property];
if (z_details.Count == 1)
{
// Special handling required for Attributes that aren't strings objectSid, objectGUID, etc
string z_value = z_details[0].ToString().Trim();
if (!string.IsNullOrWhiteSpace(z_value))
{
a_user.UserAttributes.Add(z_property, z_value);
}
}
}
}
}
}
}
}
}
}
Related
I am using GoogleAnalyticsTracker which works great and I am getting analytics reports for usage in my WinForms app.
However, every hit creates a new unique user session in Google Analytics and I would like to show unique users to give an idea of how many users are using my app.
I have read a section in the instructions but I am unsure how I implement the interface - I understand the basics of using an interface but this is something I would like to learn more here.
From the official website sessions are documented as follows:
Sessions are also untracked: every event that is tracked counts as a
new unique visitor to Google Analytics.
If you do need to track user sessions, implement a custom IAnalyticsSession and pass it to the constructor of the Tracker
object.
I would like to know how I can implement a custom IAnalyticsSession and pass it to the constructor of the Tracker object to my code below:
public class CommonTracking : IAnalyticsSession
{
static string googleID = "UA-######-##";
public string GenerateCacheBuster()
{
throw new NotImplementedException();
}
public string GenerateSessionId()
{
throw new NotImplementedException();
}
public static void TrackFeature()
{
// Get calling method name
StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
TrackFeature(methodBase.Name);
}
public static async void TrackFeature(string FeatureCustom)
{
SimpleTrackerEnvironment trackerEnvironment = new SimpleTrackerEnvironment(Environment.OSVersion.Platform.ToString(),
Environment.OSVersion.Version.ToString(),
Environment.OSVersion.VersionString);
// Overwrite platform details
KeyValuePair<string, string> kvpOSSpecs = GetOperatingSystemProductName();
trackerEnvironment.OsPlatform = kvpOSSpecs.Key;
trackerEnvironment.OsVersion = kvpOSSpecs.Value;
SimpleTracker tracker = new SimpleTracker(googleID, trackerEnvironment);
await tracker.TrackPageViewAsync(System.AppDomain.CurrentDomain.FriendlyName, FeatureCustom, null);
}
static KeyValuePair<string, string> GetOperatingSystemProductName()
{
KeyValuePair<string, string> OperatingSystemSpec = new KeyValuePair<string, string>();
ManagementObjectSearcher wmiOsInfo = new ManagementObjectSearcher("SELECT Caption, Version FROM Win32_OperatingSystem");
try
{
foreach (var os in wmiOsInfo.Get())
{
var version = os["Version"].ToString();
var productName = os["Caption"].ToString();
OperatingSystemSpec = new KeyValuePair<string, string>(productName, version);
}
}
catch { }
return OperatingSystemSpec;
}
}
Problem solved...
public class GoogleTracking : IAnalyticsSession
{
static string _GoogleAnayticsPropertyID = string.Empty;
static AnalyticsSession _Session = new AnalyticsSession();
static Dictionary<int, string> _CustomDimensions = new Dictionary<int, string>();
static int iVal = 0;
public GoogleTracking(string googleID)
{
_GoogleAnayticsPropertyID = googleID;
}
public string GenerateCacheBuster()
{
return _Session.GenerateCacheBuster();
}
public string GenerateSessionId()
{
return _Session.GenerateSessionId();
}
public void UserDefined(string strUserVal)
{
_CustomDimensions.Add(iVal++, strUserVal);
}
public static void TrackFeature()
{
StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
TrackFeature(methodBase.Name);
}
public static async void TrackFeature(string FeatureCustom)
{
if (!string.IsNullOrEmpty(_GoogleAnayticsPropertyID))
{
SimpleTrackerEnvironment trackerEnvironment = new SimpleTrackerEnvironment(Environment.OSVersion.Platform.ToString(),
Environment.OSVersion.Version.ToString(),
Environment.OSVersion.VersionString);
// Overwrite platform details
KeyValuePair<string, string> kvpOSSpecs = GetOperatingSystemProductName();
trackerEnvironment.OsPlatform = kvpOSSpecs.Key;
trackerEnvironment.OsVersion = kvpOSSpecs.Value;
SimpleTracker tracker = new SimpleTracker(_GoogleAnayticsPropertyID, _Session, trackerEnvironment);
await tracker.TrackPageViewAsync(System.AppDomain.CurrentDomain.FriendlyName, FeatureCustom, _CustomDimensions);
}
}
static KeyValuePair<string, string> GetOperatingSystemProductName()
{
KeyValuePair<string, string> OperatingSystemSpec = new KeyValuePair<string, string>();
ManagementObjectSearcher wmiOsInfo = new ManagementObjectSearcher("SELECT Caption, Version FROM Win32_OperatingSystem");
try
{
foreach (var os in wmiOsInfo.Get())
{
var version = os["Version"].ToString();
var productName = os["Caption"].ToString();
OperatingSystemSpec = new KeyValuePair<string, string>(productName, version);
}
}
catch (Exception ex)
{
Messagebox.Show(ex);
}
return OperatingSystemSpec;
}
}
I have been running into a rather frustrating issue. I am attempting to authenticate a user against an Active Directory, and in order to do so I pass my users variables into the following class.
public static ILdapAuthentication CreateInstance(string domainAndUser, string password, string ldapPath)
{
string[] dllPaths = Directory.GetFiles(ExecutingAssemblyDirectory, "*.dll");
List<Assembly> listOfAssemblies = new List<Assembly>();
foreach (var dllPath in dllPaths.Where(x => x.Contains("ActiveDirectoryAuthentication")))
{
Assembly assembly = Assembly.LoadFrom(dllPath);
listOfAssemblies.Add(assembly);
}
Type type = null;
int foundTypes = 0;
foreach (var assembly in listOfAssemblies)
{
type =
assembly.GetTypes()
.FirstOrDefault(x => x.GetInterfaces().Any(i => i == typeof(ILdapAuthentication)));
if (type == null)
continue;
foundTypes++;
}
if (foundTypes == 0)
throw new Exception("ActiveDirectoryAuthentication DLL not found.");
if (foundTypes > 1)
throw new Exception("Only one ActiveDirectoryAuthentication DLL must be used.");
return Activator.CreateInstance(type, domainAndUser, password, ldapPath) as ILdapAuthentication;
}
The issue occurs in the foreach loop, as I attempt to get my Types, it always returns null, and doesn't even hit the Interface (ILDPAuthentication) code below.
public interface ILdapAuthentication
{
bool IsActiveDirectoryUserValid();
}
which invokes the following code:
public class LdapAuthentication : ILdapAuthentication
{
private string DomainAndUser { get; set; }
private string Password { get; set; }
private string LdapPath { get; set; }
public LdapAuthentication(string domainAndUser, string password, string ldapPath)
{
this.DomainAndUser = domainAndUser;
this.Password = password;
this.LdapPath = ldapPath;
}
public bool IsActiveDirectoryUserValid()
{
try
{
if (!this.DomainAndUser.Contains('\\'))
throw new Exception("Domain User is invalid.");
string[] userLogin = this.DomainAndUser.Split('\\');
string domain = userLogin[0];
string userName = userLogin[1];
DirectoryEntry entry = new DirectoryEntry(this.LdapPath, this.DomainAndUser, this.Password);
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + userName + ")";
search.PropertiesToLoad.Add("CN");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
else
{
return true;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
}
}
The initial class looks for the DLLs in my application folder called ActiveDirectoryAuthentication which I have copied in.
I have seen this before - types in a explicitly loaded assembly do not match types in a referenced project. Because you are using:
i == typeof(ILdapAuthentication)
you are reliant on the equality comparison for the Type class, which may not return equality when you are expecting it to. I suggest instead you do:
i.FullName == typeof(ILdapAuthentication).FullName
which will use a simple string comparison.
The editor class has a method called GetString which prompts the user for a string value via AutoCAD's command prompt. I call it in this wrapper method:
public static string PromptUserForString(string message = "Enter a string: ", string defaultAnswer = "")
{
return _editor.GetString("\n" + message).StringResult;
}
The argument message becomes the message the user sees when prompted for a string. How do I set it up so that the value of default answer is automatically set to be the answer so that if the user hits enter right away that becomes the value like in the screen shot below
So 1 is automatically typed as an answer meaning the user can either hit enter for the value of 1 or change 1 to whatever non-default answer they want
I paste you some code as example for the different prompts :
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
namespace EditorUtilities
{
/// <summary>
/// Prompts with the active document ( MdiActiveDocument )
/// </summary>
public class EditorHelper : IEditorHelper
{
private readonly Editor _editor;
public EditorHelper(Document document)
{
_editor = document.Editor;
}
public PromptEntityResult PromptForObject(string promptMessage, Type allowedType, bool exactMatchOfAllowedType)
{
var polyOptions = new PromptEntityOptions(promptMessage);
polyOptions.SetRejectMessage("Entity is not of type " + allowedType);
polyOptions.AddAllowedClass(allowedType, exactMatchOfAllowedType);
var polyResult = _editor.GetEntity(polyOptions);
return polyResult;
}
public PromptPointResult PromptForPoint(string promptMessage, bool useDashedLine = false, bool useBasePoint = false, Point3d basePoint = new Point3d(),bool allowNone = true)
{
var pointOptions = new PromptPointOptions(promptMessage);
if (useBasePoint)
{
pointOptions.UseBasePoint = true;
pointOptions.BasePoint = basePoint;
pointOptions.AllowNone = allowNone;
}
if (useDashedLine)
{
pointOptions.UseDashedLine = true;
}
var pointResult = _editor.GetPoint(pointOptions);
return pointResult;
}
public PromptPointResult PromptForPoint(PromptPointOptions promptPointOptions)
{
return _editor.GetPoint(promptPointOptions);
}
public PromptDoubleResult PromptForDouble(string promptMessage, double defaultValue = 0.0)
{
var doubleOptions = new PromptDoubleOptions(promptMessage);
if (Math.Abs(defaultValue - 0.0) > Double.Epsilon)
{
doubleOptions.UseDefaultValue = true;
doubleOptions.DefaultValue = defaultValue;
}
var promptDoubleResult = _editor.GetDouble(doubleOptions);
return promptDoubleResult;
}
public PromptIntegerResult PromptForInteger(string promptMessage)
{
var promptIntResult = _editor.GetInteger(promptMessage);
return promptIntResult;
}
public PromptResult PromptForKeywordSelection(
string promptMessage, IEnumerable<string> keywords, bool allowNone, string defaultKeyword = "")
{
var promptKeywordOptions = new PromptKeywordOptions(promptMessage) { AllowNone = allowNone };
foreach (var keyword in keywords)
{
promptKeywordOptions.Keywords.Add(keyword);
}
if (defaultKeyword != "")
{
promptKeywordOptions.Keywords.Default = defaultKeyword;
}
var keywordResult = _editor.GetKeywords(promptKeywordOptions);
return keywordResult;
}
public Point3dCollection PromptForRectangle(out PromptStatus status, string promptMessage)
{
var resultRectanglePointCollection = new Point3dCollection();
var viewCornerPointResult = PromptForPoint(promptMessage);
var pointPromptStatus = viewCornerPointResult.Status;
if (viewCornerPointResult.Status == PromptStatus.OK)
{
var rectangleJig = new RectangleJig(viewCornerPointResult.Value);
var jigResult = _editor.Drag(rectangleJig);
if (jigResult.Status == PromptStatus.OK)
{
// remove duplicate point at the end of the rectangle
var polyline = rectangleJig.Polyline;
var viewPolylinePoints = GeometryUtility.GetPointsFromPolyline(polyline);
if (viewPolylinePoints.Count == 5)
{
viewPolylinePoints.RemoveAt(4); // dont know why but true, probably mirror point with the last point
}
}
pointPromptStatus = jigResult.Status;
}
status = pointPromptStatus;
return resultRectanglePointCollection;
}
public PromptSelectionResult PromptForSelection(string promptMessage = null, SelectionFilter filter = null)
{
var selectionOptions = new PromptSelectionOptions { MessageForAdding = promptMessage };
var selectionResult = String.IsNullOrEmpty(promptMessage) ? _editor.SelectAll(filter) : _editor.GetSelection(selectionOptions, filter);
return selectionResult;
}
public PromptSelectionResult PromptForSelection(PromptSelectionOptions promptSelectionOptions,SelectionFilter filter = null)
{
return _editor.GetSelection(promptSelectionOptions, filter);
}
public void WriteMessage(string message)
{
_editor.WriteMessage(message);
}
public void DrawVector(Point3d from, Point3d to, int color, bool drawHighlighted)
{
_editor.DrawVector(from, to, color, drawHighlighted);
}
}
}
I'm getting an exception
Cannot access a disposed object.
I know I'm getting this exception because the object has already been disposed before it had the chance to return the results. What I would like to know is whats the correct way to retrieve the objects. I may have unnecessary steps in my code and may be doing this all wrong?
I have a button click event in my main class. It calls the method shown in the code below. The GetADComputer method is located in another class called ActiveDirectory.cs. I'm getting my exception when attempting to access the results of the disposed object.
public static PrincipalSearchResult<Principal> GetADComputer(string pcName)
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
using (ComputerPrincipal computer = new ComputerPrincipal(ctx))
{
computer.Name = String.Format("*{0}*", pcName);
using (PrincipalSearcher searcher = new PrincipalSearcher())
{
searcher.QueryFilter = computer;
return searcher.FindAll();
}
}
}
}
The problem is DirectoryServices uses Lazy Loading for it's information, so because you closed the context you can't retrieve the information from the Principal anymore.
You have two options, either pass the PrincipalContext in to the query so it does not go out of scope when you return:
public static DoSomthing()
{
using(var ctx = new PrincipalContext(ContextType.Domain))
using(PrincipalSearchResult<Principal> result = GetADComputer("some Name", ctx))
{
//do something with the result here.
}
}
public static PrincipalSearchResult<Principal> GetADComputer(string pcName, PrincipalContext ctx)
{
using (var computer = new ComputerPrincipal(ctx))
{
computer.Name = String.Format("*{0}*", pcName);
using (var searcher = new PrincipalSearcher())
{
searcher.QueryFilter = computer;
return searcher.FindAll();
}
}
}
Or you will need to convert the results in to something that does not rely on lazy loading so you can close the connection to the directory server.
public static List<ComputerInfo> GetADComputer(string pcName, PrincipalContext ctx)
{
using (var computer = new ComputerPrincipal(ctx))
{
computer.Name = String.Format("*{0}*", pcName);
using (var searcher = new PrincipalSearcher())
{
searcher.QueryFilter = computer;
using (PrincipalSearchResult<Principal> result = searcher.FindAll())
{
return result.Select(p=> new ComputerInfo(p.Name, p.SID)).ToList();
}
}
}
}
public class ComputerInfo
{
public ComputerInfo(string name, SecurityIdentifier sid)
{
Name = name;
SID = sid;
}
public string Name {get; set;}
public SecurityIdentifier SID {get; set;}
}
would anyone have a working example of an amazon ITEMLOOKUP ?>
i have the following code but it does not seem to work:
string ISBN = "0393326381";
string ASIN = "";
if (!(string.IsNullOrEmpty(ISBN) && string.IsNullOrEmpty(ASIN)))
{
AWSECommerceServicePortTypeChannel service = new AWSECommerceServicePortTypeChannel();
ItemLookup lookup = new ItemLookup();
ItemLookupRequest request = new ItemLookupRequest();
lookup.AssociateTag = secretKey;
lookup.AWSAccessKeyId = accessKeyId;
if (string.IsNullOrEmpty(ASIN))
{
request.IdType = ItemLookupRequestIdType.ISBN;
request.ItemId = new string[] { ISBN.Replace("-", "") };
}
else
{
request.IdType = ItemLookupRequestIdType.ASIN;
request.ItemId = new string[] { ASIN };
}
request.ResponseGroup = new string[] { "OfferSummary" };
lookup.Request = new ItemLookupRequest[] { request };
response = service.ItemLookup(lookup);
if (response.Items.Length > 0 && response.Items[0].Item.Length > 0)
{
Item item = response.Items[0].Item[0];
if (item.MediumImage == null)
{
//bookImageHyperlink.Visible = false;
}
else
{
//bookImageHyperlink.ImageUrl = item.MediumImage.URL;
}
//bookImageHyperlink.NavigateUrl = item.DetailPageURL;
//bookTitleHyperlink.Text = item.ItemAttributes.Title;
//bookTitleHyperlink.NavigateUrl = item.DetailPageURL;
if (item.OfferSummary.LowestNewPrice == null)
{
if (item.OfferSummary.LowestUsedPrice == null)
{
//priceHyperlink.Visible = false;
}
else
{
//priceHyperlink.Text = string.Format("Buy used {0}", item.OfferSummary.LowestUsedPrice.FormattedPrice);
//priceHyperlink.NavigateUrl = item.DetailPageURL;
}
}
else
{
//priceHyperlink.Text = string.Format("Buy new {0}", item.OfferSummary.LowestNewPrice.FormattedPrice);
//priceHyperlink.NavigateUrl = item.DetailPageURL;
}
if (item.ItemAttributes.Author != null)
{
//authorLabel.Text = string.Format("By {0}", string.Join(", ", item.ItemAttributes.Author));
}
else
{
//authorLabel.Text = string.Format("By {0}", string.Join(", ", item.ItemAttributes.Creator.Select(c => c.Value).ToArray()));
}
/*
ItemLink link = item.ItemLinks.Where(i => i.Description.Contains("Wishlist")).FirstOrDefault();
if (link == null)
{
//wishListHyperlink.Visible = false;
}
else
{
//wishListHyperlink.NavigateUrl = link.URL;
}
* */
}
}
}
the problem is with this:
thisshould be defined differently but i do not know how AWSECommerceServicePortTypeChannel service = new AWSECommerceServicePortTypeChannel();
Say, that code looks awful familiar. You're missing the Endpoint signing piece from when they switched over to requiring that you add message signing. You need to add a behavior on your client. Here's the change to your code above:
if (!(string.IsNullOrEmpty(ISBN) && string.IsNullOrEmpty(ASIN)))
{
AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient();
client.ChannelFactory.Endpoint.Behaviors.Add(
new Amazon.AmazonSigningEndpointBehavior(
accessKeyId,
secretKey);
ItemLookup lookup = new ItemLookup();
ItemLookupRequest request = new ItemLookupRequest();
lookup.AssociateTag = accessKeyId;
lookup.AWSAccessKeyId = secretKey;
//... etc.
And here's the Endpoint (I can't take credit for this, I wish I could remember who should):
namespace Amazon
{
public class AmazonSigningEndpointBehavior : IEndpointBehavior {
private string accessKeyId = "";
private string secretKey = "";
public AmazonSigningEndpointBehavior(string accessKeyId, string secretKey) {
this.accessKeyId = accessKeyId;
this.secretKey = secretKey;
}
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(new AmazonSigningMessageInspector(accessKeyId, secretKey));
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { return; }
public void Validate(ServiceEndpoint serviceEndpoint) { return; }
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters) { return; }
}
}
Oh. And you'll need the MessageInspector for that to work.
namespace Amazon
{
public class AmazonSigningMessageInspector : IClientMessageInspector {
private string accessKeyId = "";
private string secretKey = "";
public AmazonSigningMessageInspector(string accessKeyId, string secretKey) {
this.accessKeyId = accessKeyId;
this.secretKey = secretKey;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
// prepare the data to sign
string operation = Regex.Match(request.Headers.Action, "[^/]+$").ToString();
DateTime now = DateTime.UtcNow;
string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");
string signMe = operation + timestamp;
byte[] bytesToSign = Encoding.UTF8.GetBytes(signMe);
// sign the data
byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
HMAC hmacSha256 = new HMACSHA256(secretKeyBytes);
byte[] hashBytes = hmacSha256.ComputeHash(bytesToSign);
string signature = Convert.ToBase64String(hashBytes);
// add the signature information to the request headers
request.Headers.Add(new AmazonHeader("AWSAccessKeyId", accessKeyId));
request.Headers.Add(new AmazonHeader("Timestamp", timestamp));
request.Headers.Add(new AmazonHeader("Signature", signature));
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState) { }
}
}
And finally, the Header:
namespace Amazon
{
public class AmazonHeader : MessageHeader
{
private string name;
private string value;
public AmazonHeader(string name, string value)
{
this.name = name;
this.value = value;
}
public override string Name { get { return name; } }
public override string Namespace { get { return "http://security.amazonaws.com/doc/2007-01-01/"; } }
protected override void OnWriteHeaderContents(XmlDictionaryWriter xmlDictionaryWriter, MessageVersion messageVersion)
{
xmlDictionaryWriter.WriteString(value);
}
}
}
Yes, they made it complicated when they started requiring message signing...
A simple and easy library is available on nuget.
PM> Install-Package Nager.AmazonProductAdvertising
Example
var authentication = new AmazonAuthentication("accesskey", "secretkey");
var client = new AmazonProductAdvertisingClient(authentication, AmazonEndpoint.US);
var result = await client.GetItemsAsync("B00BYPW00I");
To perform a lookup for anything other then an ASIN, you need to specify the "SearchIndex" property. You can simply set it to "All".
var request = new ItemLookupRequest();
request.ItemId = new[] {upcCode};
request.IdType = ItemLookupRequestIdType.UPC;
request.IdTypeSpecified = true;
request.SearchIndex = "All";
Here is a link to the documentation: http://docs.amazonwebservices.com/AWSECommerceService/2011-08-01/DG/index.html?ItemLookup.html. Note the description of the SearchIndex parameter:
Constraint:If ItemIdis an ASIN, a search index cannot be specified in
the request. Required for non-ASIN ItemIds.
I actually built a little wrapper around it so it hands you back a handy object graph. I have the source up on BitBucket and a little more about it on the C# Amazon ItemLookup page.
C# Amazon ItemLookup
You can make calls like:
var item = client.LookupByAsin("B0037X9N5U");
double? price = item.GetLowestPrice();