Hi All I have used Odata4j to create Odata Service and deployed in tomcat. When i use Sesame Data Browser i can see a Table (if i click on THREAD ) having header.
My question what should be the url to see the same data in web-browser? I want to consume this in a service so want to know url.
If i type this in http://localhost:8888/OdataEx/example.svc browser i can see some XML
<?xml version="1.0" encoding="utf-8" ?>
<service xmlns="http://www.w3.org/2007/app" xml:base="http://localhost:8888/OdataEx/example.svc/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app">
<workspace>
<atom:title>Default</atom:title>
<collection href="Threads">
<atom:title>Threads</atom:title>
</collection>
</workspace>
</service>
and Java code generating service is
public class ExampleProducerFactory implements ODataProducerFactory {
public ODataProducer create(Properties properties) {
InMemoryProducer producer = new InMemoryProducer("example");
// expose this jvm's thread information (Thread instances) as an entity-set called "Threads"
producer.register(Thread.class, Long.class, "Threads", new Func<Iterable<Thread>>() {
public Iterable<Thread> apply() {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
while (tg.getParent() != null)
tg = tg.getParent();
Thread[] threads = new Thread[50];
int count = tg.enumerate(threads, true);
return Enumerable.create(threads).take(count);
}
}, Funcs.method(Thread.class, Long.class, "getId"));
return producer;
}
}
To see a specific entity set just append the entity set name to the URL. For example:
The sample service URL is: http://services.odata.org/OData/OData.svc/
To see the Products entity set, go to: http://services.odata.org/OData/OData.svc/Products
Note that this is also described in the service document returned by the .svc directly. Each collection element has an href attribute which is a relative URL pointing to that collection.
If you want to see data from browser then just add entityset name after .svc. you can also put the query to see the filterd data
The sample service URL is: http://services.odata.org/OData/OData.svc/
http://services.odata.org/OData/OData.svc/entitynameset
Related
First I would like to describe our architecture, it consists of:
Outlook Web Addin (hosted on IIS on-prem)
an ASP.NET Web API (hosted on-prem)
the Microsoft Graph REST API in the cloud
a SOAP WebService (not under my control, on-prem).
The flow can be described with these steps:
User clicks a button in the Outlook Web Add-in
An Ajax request is made from the Web Add-in to the Web API with the ID of the message as a parameter.
The Web API requests the message from Graph via GraphServiceClient
The Web API packs the attachments from the message into a SOAP request and sends submits it to the SOAP service.
The attachments in Microsoft Graph are returned as a Byte Array and stored in memory. So we can get run into memory and performance issues.
My question is, would it be possible to stream the attachments from Microsoft Graph directly to the SOAP service?
Getting attachments from MS Graph Rest API
var mail = graphClient
.Users["mailbox"]
.Messages["id"]
.Request()
.Expand("attachments");
foreach (var attachment in message.Attachments)
{
if (attachment.GetType() == typeof(FileAttachment))
{
var fileAttachment = (FileAttachment) attachment;
// ==> fileAttachment.ContentBytes already contains the bytes of the attachment
}
}
Should the bytes of the attachment be fetched with a second request?
How can it be streamed down?
The definition of the attachment part in the soap service wsdl
<xsd:complexType name="DocumentData">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="extension" type="xsd:string"/>
<xsd:element name="content" type="xsd:base64Binary"/>
</xsd:sequence>
</xsd:complexType>
... is generated in the service reference as
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3062.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://is.uniqa.at/UniqaDocumentWF.WS:startDoWFProcess")]
public partial class DocumentData : object, System.ComponentModel.INotifyPropertyChanged
{
private string nameField;
private string extensionField;
private byte[] contentField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]
public string name
{
get
{
return this.nameField;
}
set
{
this.nameField = value;
this.RaisePropertyChanged("name");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 1)]
public string extension
{
get
{
return this.extensionField;
}
set
{
this.extensionField = value;
this.RaisePropertyChanged("extension");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, DataType = "base64Binary", Order = 2)]
public byte[] content
{
get
{
return this.contentField;
}
set
{
this.contentField = value;
this.RaisePropertyChanged("content");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
... and can be created like this:
DoWF.DocumentData data = new DocumentData();
data.name = name;
data.extension = extension;
// At the moment content is set to
// "fileAttachment.ContentBytes" before
// the request is send to the soap service
data.content = content;
How and where can I stream the attachment bytes from Rest API over "my" Web API and then to the SOAP service?
I would also be grateful for a completely different approach to solve this problem.
Given the number of layers involved and your inability to control the APIs on either end of the workflow, I'm not sure this approach would work. More importantly, I suspect even if you can get this working, you'll find it extremely fragile.
I would suggest something along the lines of this article from the documentation: Get attachments of an Outlook item from the server. This uses example uses Exchange Web Services rather than Microsoft Graph but the general flow would be the same:
User clicks the button in the Add-in
Add-in passes the userPrincipalName and the message id to Web API
Web API downloads the attachments to temporary storage.
Web API uploads the attachments from temporary storage to the SOAP service.
In order to make this work, your Web API will need to use Client Credentials to connect to Microsoft Graph using the Mail.Read application scope. This will allow the Web API to connect to any mailbox and download the attachments.
I'm having problems defining a function for odata4. The default get would work but I want to require a user parameter so a client set can be determined, other tables are involved so LINQ is required, I also return a DTO instead of the default table info (EF). Below is the code. I get a "Invalid EntitySetPath detected. 'bindingParameter/Client' is not a valid entity set path for procedure 'Default.GetClients'." What am I doing wrong here?
WebApiConfig
public static void Register(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<client>("Client").EntityType.HasKey(p => p.int_id);
var function = builder.Function("GetClients");
function.Parameter<string>("user");
function.ReturnsCollectionFromEntitySet<client>("Client");
builder.EntitySet<ClientDTO>("ClientDTO");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
WebApp.Controller
[ODataRoute("GetClients(user={user})")]
[EnableQuery(PageSize=25)]
public IQueryable<ClientDTO> GetClients([FromODataUri] string user)
{
var clients = (from c in db.clients
join ...
If your OData controller is returning the DTO, the function should look like this:
var function = builder.Function("GetClients");
function.Parameter<string>("user");
function.ReturnsCollectionFromEntitySet<ClientDTO>("Client");
With your current setup, your OData route of GetClients says that it is returning a ClientDTO object, but your WebApiConfig is stating you are returning a Client object.
As the Entity Collection being returned is actually the DTO. The part that shows ("Client") is simply how the OData service will report the name of the object to the project consuming the OData service. For my own personal sanity, I typically include DTO as well so I know when I'm using a DTO and when I'm using a direct entity. So in my own setup i'd return ("ClientDTO"), just a personal preference.
In my project I need to call a web service from a controller. I have already done the following, and it works.
Add the web reference for the web service to the project.
Call the service as follows:
Service Wservice=new Service();
Wservice.loginCompleted+=new Wservice_login_Completed;
WService.login_Async("username","Password");
Note: Whenever i call this service it throws an error that is
"An asynchronous operation cannot be started at this time.
Asynchronous operations may only be started within an asynchronous handler
or module or during certain events in the Page lifecycle. If this
exception occurred while executing a Page, ensure that the Page is marked
<%# Page Async="true" %>."
To overcome this issue I use
[Httppost]
public ActionResult login(logmodel model)
{
Task.Factory.StartNew(() =>
{
Wservice.loginCompleted+=new Wservice_login_Completed;
WService.login_Async("username","Password");
});
if(finalresult==true)
{
*** return View();
}
}
void Wservice_login_completed()
{
Here i got the output.
}
But the calling of Wservice_login_completed() function was after the View*** was returned,, so I'm not getting the result. How do I achieve "calling webservice from Controller".. Any ideas?
Finally i called the webservice from MVC Controller Successfully.
Note: Add ServiceReference instead of WebReference and avoid
"Task.Factory.StartNew(()=>);" Process.
[Httppost]
public ActionResult login(logmodel model)
{
Wservice.ServiceSoapClient _host = new Wservice.ServiceSoapClient("ServiceSoap");
var result_out = _host.login(uname, pwd, "test1", "test2", "test3", "test4");
}
Here "ServiceSoap" is an endpoint for our service.. you may got the endpoint to be presented in app.confiq or web.config files.
Happy Coding...!
get the following NuGet:
microsoft http client
(id = Microsoft.Net.Http)
Create a Web API Controller (webapi_Controller_Name)
Your Post function should be similar to the following function
Put this function in your Web Api Controller
[HttpPost]
public void PostForm(objUser ws_Obj)
{
// put you code here
}
Call your Web Service from you regular Controller as follows.
This is an Async call and will Web Service will return immediately.
//call the web service, Asynch
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("52323/");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.PostAsJsonAsync("//52323/api/webapi_Controller_Name/PostForm", objContact);
First, create a service reference by right clicking on the project name in the solution explorer, then hover over the "Add" option and click on "Service Reference..."
Second, paste your web service address in the "Address" field of the "Add Service Reference" page, be sure to add "?wsdl" to the end of your web service address or it won't work, and then press "Go". You will see the web service appear in the "Services" area. Click on the service to see the available services which will appear in the "Operations" section. Rename the service if you want, then press OK to create the service.
Finally, put the following code in your MVC Controller. Place the code in a Get or Post controller, it doesn't matter.
// Declare the Request object.
ServiceReference1.GetSurveyRequest myGSRq = new ServiceReference1.GetSurveyRequest();
// You can set the webservice parameters here like...
myGSRq.Address = getUserData[0].address;
myGSRq.City = getUserData[0].city;
myGSRq.State = getUserData[0].state;
// Now declare the Response object.
ServiceReference1.GetSurveyResponse myGSR = new ServiceReference1.GetSurveyResponse();
//And then use the following to process the request and response.
ServiceReference1.EMPortTypeClient emptc = new ServiceReference1.EMPortTypeClient();
myGSR = emptc.GetSurvey(myGSRq);
// In this example the response Object comes back with a URL used to redirect the user
//after the webservice has been processed.
if (myGSR.Url != null)
Response.Redirect(myGSR.Url.ToString());
else
Response.Write("Error");
return null;
Pretty simple, hope this helps!
If you are creating a new service and have the option of using a webservice or a Web API, I suggest using Web API.
Build RESTful API's with ASP.NET Web API
I'm new to Project Server development and was wondering if you can use the PSI to set resource custom field values.
I can't seem to find any information for a beginner at this.
I currently have web references set up for the CustomFields and the Resource web service, but am unsure how to set a custom field for a particular resource.
Any help would be really appreciated!
Thanks!
I know your problem. Microsoft has really bad examples on MSDN. Lot of stuff doesn't work or has just been copied from the 2007 manual. I've started yesterday to develop with the Webservice for Project Server 2010.
Here is a simple (maybe not the best) solution for setting custom fields:
Complete source code: http://pastebin.com/tr7CGJsW
Prepare your solution
First I've added a Service Reference to:
http: //servername/instance/_vti_bin/PSI/Project.asmx?wsdl
After that, I've edited the app.config because I had to use special credentials to login into the project server (maybe this is not necessarily for you):
...
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="Ntlm" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
<!--<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>-->
</binding>
The commented code has been created by Visual Studio
Connect and Update
Now we can create a new SoapClient which communicates with the Project Server:
//Creating a new service client object
ProjectSoapClient projectSvc = new ProjectSoapClient();
//Just if you need to authenticate with another account!
projectSvc.ClientCredentials.Windows.ClientCredential = new NetworkCredential("test", "test", "demo");
projectSvc.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
Now I have declared two Guids, that we know, what we want to update. The other variables are used later and are comment in the code:
//Guid of my project
Guid myProjectId = new Guid("{610c820f-dc74-476c-b797-1e61a77ed6c6}");
//Guid of the custom field
Guid myCustomFieldId = new Guid("{cd879634-b3ee-44eb-87f7-3063a3523f45}");
//creating a new sessionId and a new jobId
Guid sessionId = Guid.NewGuid(); //the sessionId stays for the whole updating process
Guid jobId = Guid.NewGuid(); //for each job, you give to the server, you need a new one
//indicator if you have to update the project
Boolean updatedata = false;
Then we are ready to load the ProjectDataSet from the server, find the CustomField and updating the data. It's really simple:
loading the ProjectDataSet
iterate trough the CustomFieldsRow
checking if the CustomField matches with our Guid
updating the value
setting the indicator to update
//loading project data from server
//Every change on this dataset will be updated on the server!
ProjectDataSet project = projectSvc.ReadProject(myProjectId, DataStoreEnum.WorkingStore);
//To find your custom field, you have to search for it in the CustomFieldsRow
foreach (ProjectServerCSVImport.PSS.Project.ProjectDataSet.ProjectCustomFieldsRow row in project.ProjectCustomFields)
{
//check if the GUID is the same
if (row.MD_PROP_UID == myCustomFieldId)
{
//if yes, write it into the container
row.NUM_VALUE = 12345;
//and set the indicater
updatedata = true;
}
}
If we changed a value, we have to send the ProjectDataSet to the ProjectServer now. It will update the changed values in the ProjectDataSet. For this, we have to check out our project, update it and check in again:
//update if you have changed anything
if (updatedata)
{
//check out the project first
projectSvc.CheckOutProject(myProjectId, sessionId, "custom field update checkout");
//send the dataset to the server to update the database
bool validateOnly = false;
projectSvc.QueueUpdateProject(jobId, sessionId, project, validateOnly);
//wait 4 seconds just to be sure the job has been done
System.Threading.Thread.Sleep(4000);
//create a new jobId to check in the project
jobId = Guid.NewGuid();
//CheckIn
bool force = false;
string sessionDescription = "updated custom fields";
projectSvc.QueueCheckInProject(jobId, myProjectId, force, sessionId, sessionDescription);
//wait again 4 seconds
System.Threading.Thread.Sleep(4000);
But now we just have updated the database. The server will still show us the "old" value and not the new one. This is because we didn't have published our project yet:
//again a new jobId to publish the project
jobId = Guid.NewGuid();
bool fullPublish = true;
projectSvc.QueuePublish(jobId, myProjectId, fullPublish, null);
//maybe we should wait again ;)
System.Threading.Thread.Sleep(4000);
Finally we're done and our project is updated!
Enhancements
I'm very new to this platform (Project Server 2010), so this code maybe is not the best example. So there are some enhancements, which would made the solution better:
The Sleep function is a very bad workaround, but I couldn't find an example which handles the same thing (like here) with a QueueSystem for 2010?!
If you don't know the Guid of your project, but the name, you can resolve it by following function:
/// <summary>
/// Returns the GUID for a specified project
/// and sets the guid for this class
/// </summary>
/// <param name="client">soap service client</param>
/// <param name="projectname">name of the project</param>
/// <returns>Project GUID</returns>
public Guid GetGuidByProjectName(ProjectSoapClient client, string projectname)
{
Guid pguid = new Guid();
ProjectDataSet data = client.ReadProjectList();
foreach (DataRow row in data.Tables[0].Rows)
{
if (row[1].ToString() == projectname) //compare - case sensitive!
{
pguid = new Guid(row[0].ToString());
}
}
return pguid;
}
I'm hosting an ASP.NET MVC site on Winhost and recently added a WCF data service for users providing feedback. The service works fine from my local machine, but when I deploy it I get the following System.NullReferenceException returned in Fiddler whenever I try to save an entry to the service:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code></code>
<message xml:lang="en-US">An error occurred while processing this request.</message>
<innererror>
<message>Object reference not set to an instance of an object.</message>
<type>System.NullReferenceException</type>
<stacktrace> at System.Data.Services.Providers.ObjectContextServiceProvider.SetValue(Object targetResource, String propertyName, Object propertyValue)
at System.Data.Services.Serializers.Deserializer.SetPropertyValue(ResourceProperty resourceProperty, Object declaringResource, Object propertyValue, ContentFormat contentFormat, IDataService service)
at System.Data.Services.Serializers.PlainXmlDeserializer.ApplyProperty(XmlReader reader, String propertyName, ResourceType resourceType, Object resource)
at System.Data.Services.Serializers.PlainXmlDeserializer.ApplyContent(XmlReader reader, ResourceType resourceType, Object resource)
at System.Data.Services.Serializers.PlainXmlDeserializer.ApplyContent(Deserializer deserializer, XmlReader reader, ResourceType resourceType, Object resource, EpmAppliedPropertyInfo propertiesApplied, Int32 currentObjectCount)
at System.Data.Services.Serializers.SyndicationDeserializer.ApplyProperties(SyndicationItem item, ResourceType resourceType, EpmAppliedPropertyInfo propertiesApplied, Object resource)
at System.Data.Services.Serializers.SyndicationDeserializer.CreateObject(SegmentInfo segmentInfo, Boolean topLevel, SyndicationItem item)
at System.Data.Services.Serializers.SyndicationDeserializer.CreateSingleObject(SegmentInfo segmentInfo)
at System.Data.Services.Serializers.Deserializer.ReadEntity(RequestDescription requestDescription)
at System.Data.Services.Serializers.Deserializer.HandlePostRequest(RequestDescription requestDescription)
at System.Data.Services.DataService`1.HandlePostOperation(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService`1.ProcessIncomingRequest(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService`1.HandleNonBatchRequest(RequestDescription description)
at System.Data.Services.DataService`1.HandleRequest()</stacktrace>
</innererror>
</error>
Turn on tracing both on the server and on the client (see http://msdn.microsoft.com/en-us/library/ms733025.aspx), then use SvcTraceViewer (http://msdn.microsoft.com/en-us/library/ms732023.aspx). It will usually give you a more detailed exception.
My problem turned out to be 2 issues:
My site reroutes Url's for all GET requests to lower case. ODATA queries are case sensitive. This was causing me trouble with diagnosing the problem but wasn't causing the POST issue. Removing this for my service uri helped me identify the real problem.
The real issue was that my DataContext for my POCO entities had proxy generation enabled.
Here's a lengthier description of how I found the problem in case it helps anyone debug a similar issue:
Per Dan's suggestion, I enabled tracing to see if I could get any additional details. This showed me that I was getting a System.ServiceModel.CommunicationObjectAbortedException. I searched for the cause of this for a while with no success.
Next, I set my EntitySetRights for my DataService to EntitySetRights.All to see if I could determine the error by attempting to read my entity set (note that in my case this is a temporary change for debugging).
static public void InitializeService(DataServiceConfiguration config) {
...
config.SetEntitySetAccessRule("myentityset", EntitySetRights.All);
...
}
I then queried the service http://mydomain.com/myservice.svc/myentityset and received the following error:
Resource not found for the segment 'myentityset'
This turned out to be due to my site forcing all urls to lower case (apparently ODATA queries are case sensitive). My entity set is named something like 'MyEntitySet', but it was being queried as 'myentityset' Once I disabled this for my service url, I received an error similar to the following:
Internal Server Error. The type 'System.Data.Entity.DynamicProxies.myentity_XXXX' is not a complex type or an entity type
Looking up this error led me to this link identifying my core problem. The problem is with POCO Entity Generation for DataServices. In order to fix the problem ProxyCreationEnabled needs to be set to false. I added the following method to my T4 template that generates my ObjectContext.
<#+
private void WriteProxyCreation()
{
#>
this.ContextOptions.ProxyCreationEnabled = false;
<#+
}
#>
I then added a call to my method in each of the constructors similar to the following:
public <#=code.Escape(container)#>()
: base(ConnectionString, ContainerName)
{
<#
WriteLazyLoadingEnabled(container);
WriteProxyCreation();
#>
}