Azure datafactory: Headers in REST dataset with C# - c#

I'm currently building a datafactory that needs to be rolled out to a whole slew of tenants. So I've opted to write C# code to create all the datafactory artifacts using a configuration file.
I've got almost everything done. I just run into a snag when creating the REST stuff. I need to add Headers to a WebActivity and to a REST Dataset.
I've tried doing this using a string which resulted in every character in the string becoming a separate header.
I've tried doing this using the HeaderOption class which resulted in... well... nothing. Empty headers in the DataFactory.
What is the best way to go about this?
Example code for the WebActivity (I'm assuming that getting this to work will mean getting the Dataset to work as well);
new WebActivity
{
Name = config.TokenActionName,
Method = config.TokenMethod,
Url = config.TokenUrl,
Headers = config.TokenHeader,
Body = restBody
},
Where config.TokenHeader is either
config.TokenHeader = "\"Content-Type\": \"application/x-www-form-urlencoded\""
or
config.TokenHeader = new HeaderOption("Content-Type", "application/x-www-form-urlencoded");

Related

Azure Custom Vision API returning different results than project portal?

I've create a custom vision project to recognise characters (A, B, C...).
What is interesting: if I upload an image of a character (in this case an "N") to the vision API portal it will tell me that it is 99.9% sure it is an "N":
If however I use the client libraries to predict the very same image, I'm getting 53% that it is a "W" and only 37% that it is an "N":
I double checked that the latest iteration is the published one
I double checked that I'm using the correct project ID
My endpoint is set to "https://westeurope.api.cognitive.microsoft.com" in the CustomVisionPredictionClient
The code to get the prediction on my client:
var client = new CustomVisionPredictionClient()
{
ApiKey = predictionKey,
Endpoint = endpoint
};
var result = await client.PredictImageAsync(Guid.Parse(projectId), imageStream).ConfigureAwait(false);
var prediction = result.Predictions.FirstOrDefault();
Where does this difference come from and how to fix because according to the tests I did by uploading images the results are close to 100% correct no matter which character image I upload?
UPDATE: I noticed that there was an update for the client libraries. They went from 0.12pre to 1.0stable. After the update the PredictImageAsync is gone and replaced with DetectImageAsync. This expected as an additional parameter a model name. I tried using the name of the iteration and after a while the method returns with an internal server error. So not sure what to try next.
The comment above pointed my into the right direction - thanks!
The new client library has got two methods ClassifyImage and DetectImage (and various variations of them) which replace the previously used ones including PredictImage which I was using with the preview version of the client library.
To classify an image (which is what I wanted to do) ClassifyImage should of course be used. The new code looks like this and delivers an almost 100% correct prediction:
var client = new CustomVisionPredictionClient()
{
ApiKey = predictionKey,
Endpoint = endpoint
};
var result = await client.ClassifyImageAsync(Guid.Parse(projectId), "Iteration12", imageStream).ConfigureAwait(false);
var prediction = result.Predictions.FirstOrDefault();
endpoint is the URL of the region the vision API is hosted in, in my case https://westeurope.api.cognitive.microsoft.com.
predictionKey is available on the CustomVision.AI site in your project, so is the projectId
The publishedName parameter is the name of the iteration to use (in my case "Iteration12"

VersionOne: Getting message that VersionOneAPIConnector is obsolete and I should use V1Connector instead. But how?

I am trying to upgrade my c# programs to VersionOne.SDK.API version 15.3.0.0. I am getting a message that VersionOneAPIConnector is obsolete and that I should use V1Connector instead. When the ObjectModel API was discontinued, I had changed to doing everything with VersionOneAPIConnector using query.v1 (using HttpPost) and rest-1.v1/Data(using SendData). After getting the new version, I figured out how to use V1Connector and pass it to Services and then to use ExecutePassThroughQuery() to pass what had been the 2nd argument to HttpPost for my query.v1 code like this:
Original code:
Stream resultStream = _cx.HttpPost("/query.v1", System.Text.Encoding.UTF8.GetBytes(GetQueryForMembers()));
using (StreamReader reader = new StreamReader(resultStream, Encoding.UTF8))
{
result = reader.ReadToEnd();
}
New Code:
result = _service.ExecutePassThroughQuery(GetQueryForMembers());
Where GetQueryforMembers() is:
public static string GetQueryForMembers()
{
return #"
from: Member
select:
- Email
where:
AssetState: Active";
}
But, I can't figure out how to use V1Connector/IServices for my rest/SendData code. All the Rest examples on the VersionOne site now show using it from the API Console, not from stand-alone code. It looks like at least some of the things I was doing with Rest can be done via Services.executeOperation() or Services.Meta, but the V1 employee that I was working with to get rid of my Object Model code a few years ago had recommended only using the API for the connection and to otherwise use Rest and Query, so that I didn't have to worry about compatibility with .NET versions. But, from looking at the examples currently provided and the functionality available on the V1Connector/IServices classes, it looks like maybe V1 wants us to use Meta API rather than REST now? Or is there a way to use REST directly that I am missing? From looking at the source, I see there is an internal UseDataAPI() on the V1Connector that IServices uses. But, it only uses it in Save, ExecuteOperation and Retrieve and it doesn't look like any of those do what I'm trying to do.
This is an example, if what I was doing before with REST (where _passed_cx is a VersionOneAPIConnector)
string newDefect = string.Concat("<Asset href=\"/MentorG01/rest-1.v1/New/Defect\">",
"<Attribute name=\"Name\" act=\"set\">", cdata_attribute_value, "</Attribute>",
"<Relation name=\"Scope\" act=\"set\">",
"<Asset href=\"/MentorG01/rest-1.v1/Data/Scope/", project_oid.Split(':')[1], "\" idref=\"", project_oid, "\" />",
"</Relation>",
"</Asset>");
string url = "rest-1.v1/Data/Defect";
Stream resultStream = _passed_cx.SendData(url, newDefect);
Anyone know how to use rest-1.v1 with V1Connector? If so, can you provide an example?

Send and return variable with c# API call?

I have a c# script task in an ssis package designed to geocode data through my company's proprietary system. It currently works like this:
1) Pull query of addresses and put in data table
2) Loop through that table and Foreach row, build request, send request, wait for response, then insert back into the database.
The issue is that each call takes forever to return, because before going out and getting a new address on the api side, it checks a current database(string match) to ensure the address does not already exist. If not exists, then go out and get me new data from a service like google.
Because I'm doing one at a time, it makes it easy to keep the ID field with the record when I go back to insert it into the database.
Now comes the issue at hand... I was told to configure this as multi-thread or asynchronous. Here is the page I was reading on here about this topic:
ASP.NET Multithreading Web Requests
var urls = new List<string>();
var results = new ConcurrentBag<OccupationSearch>();
Parallel.ForEach(urls, url =>
{
WebRequest request = WebRequest.Create(requestUrl);
string response = new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd();
var result = JsonSerializer().Deserialize<OccupationSearch>(new JsonTextReader(new StringReader(response)));
results.Add(result);
});
Perhaps I'm thinking about this wrong, but if I send 2 requests(A & B) and lets say B actually returns first, how can I ensure that when I go back to update my database I'm updating the correct record? Can I send the ID with the API call and return it?
My thoughts are to create an array of requests, burn through them without waiting for a response and return those value in another array, that I will then loop through on my insert statement.
Is this a good way of going about this? I've never used Parrallel.ForEach, and all the info I find on it is too technical for me to visualize and apply to my situation.
Perhaps I'm thinking about this wrong, but if I send 2 requests(A & B) and lets say B actually returns first, how can I ensure that when I go back to update my database I'm updating the correct record? Can I send the ID with the API call and return it?
None of your code contains anything that looks like an "ID," but I assume everything you need is in the URL. If that is the case, one simple answer is to use a Dictionary instead of a Bag.
List<string> urls = GetListOfUrlsFromSomewhere();
var results = new ConcurrentDictionary<string, OccupationSearch>();
Parallel.ForEach(urls.Distinct(), url =>
{
WebRequest request = WebRequest.Create(url);
string response = new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd();
var result = JsonSerializer().Deserialize<OccupationSearch>(new JsonTextReader(new StringReader(response)));
results.TryAdd(url, result);
});
After this code is done, the results dictionary will contain entries that correlate each response back to the original URL.
Note: you might want to use HttpClient instead of WebClient, and you should take care to dispose of your disposable objects, e.g. StreamReader and StringReader.

Adding a SOAP Header from a string to a request built from WSDL

I'm connecting to a WSDL through visual studio to build a web request in c#, but having an issue with authentication. The company we're working with requires information to be in the header, but doesn't expose the information they need through the WSDL, saying that, "this exact header needs to be included in all requests".
Theoretically I'd like to just get the header built in a StringBuilder object and attached to the client object, but I can't see how to do that easily. Everything I use, like WCFExtras+ or even just System.ServiceModel.Channels.MessageHeader wants an object, values, and a ns (which is obvious with C# being an OOPL).
I tried doing something like this, but i realized you can't just add XML manually like this, it strips out < and > into web-safe text equivalents:
using (var client = new productsClient())
{
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
MessageHeader<string> header = new MessageHeader<string>("wsse:Security", true, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", true);
var untyped = header.GetUntypedHeader(hardcodedHeader, "http://api.xxxcon.com/xxx");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
var request = new productRequest();
response = client.getProduct(request);
}
}
Perhaps using the MessageHeader.CreateHeader is the answer? I can use this to create a simple header that has one line
(in the shape of value) but when i try to nest the requests together to create a parent and child relationship:
var username = System.ServiceModel.Channels.MessageHeader.CreateHeader
("wsse:UsernameToken ", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
, usernameString);
var pw = System.ServiceModel.Channels.MessageHeader.CreateHeader
("wsse:Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
, passwordString);
var header1 = System.ServiceModel.Channels.MessageHeader.CreateHeader
("wsse:UsernameToken ", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
, "", username);
But again, this throws an error. I suspect this class wasn't meant to nest xml objects like this.
All in all, I'd like to actually use the WSDL and not have to manually generate all the XML, but it seems i'm hindered by object-orientation, when i really want to just say, "here's the header, just trust me that everything is there. slap this baby right on".
Is anybody familiar with what to do in this circumstance? Thank you in advance.

Updating project parameters in AtTask using restsharp put method

I am struggling a little in my project with Attask.
My aim is to update the alignmentValues of a project using custom form data..
I have been able to create new alignment values to update but I am unable to execute it using put method...
the request I want to execute is
PUT /attask/api/project/4c7...?updates=
{
alignmentValues: [
{
scoreCardOptionID: "2222...54d0",
scoreCardQuestionID : "8897...54d1",...
},....
]
}
my code snippet is
var request = new RestRequest("project/{id}", Method.PUT);
request.AddUrlSegment("id", pid);
request.RequestFormat = DataFormat.Json;
JObject _putData = new JObject();
_putData.Add("alignmentValues",newAnswers);
and for updates object I tried few combinations
request.AddParameter("updates",_putData,ParameterType.RequestBody); //no effect
request.AddBody(new {name = "updates", value = _putData}); //no effect
With this body approach I am even unable to update the name of project.
But when I supply the parameters as query string, it successfully updates the name but fails for alignment values as the url becomes too large
var request = new RestRequest("project/{id}?updates=" + _putData , Method.PUT);
Above works if _putData is small...like name = "TEST"..but fails for big json array..
Any suggestions on how to update values using addbody/addobject/addjsonobject/addparameter...because I need to send request in body because of its large size...
Thanks in advance.
Well this is not an answer but I made it to work by fluke..
SO the main problem still remains..I can't use body with PUT request..Even if I use its shows no result..so I had to go with query string only...
Now for my problem on big parameters in the query string, I was sending the whole alignmentvalues object where I had to update only two fields within that object.
So in a trial and error approach i only passed three fields within the object - answer ID and two fields to be updated...And it reduced the query string size and fortunately for me it worked..

Categories