I have an issue with the method Server.MapPath.
I am currently using the method HttpContext.Current.Server.MapPath("MyPath") to obtain the physical path of "MyPath" in an ApiController, get method.
If I try to use the same method in an ApiController which returns an HttpResponse, it gives the error:
Current is not a member of System.Net.Http.HttpContent
How can I use the method Server.MapPath in the context of an HttpResponseMessage?
(I am working in Visual Basic)
Edit: I am using it like this:
<HttpGet>
Public Function MyFunc() As HttpResponseMessage
Try
Dim streamContent = New PushStreamContent(_
Function(outputStream, httpContext, transportContent)
Try
Dim lPath = httpContext.Current.Server.MapPath(MyPath)
.... some code
End Try
End Function)
Dim lResult = New HttpResponseMessage(HttpStatusCode.OK)
lResult.Content = streamContent
Return lResult
End Try
End Function
The second argument passed to your callback function is of type HttpContent. You're effectively hiding HttpContext inside your lambda because you're named that parameter HttpContext. Try:
Dim streamContent = New PushStreamContent(_
Function(outputStream, content, transportContent) 'Renamed parameter here
Try
Dim lPath = HttpContext.Current.Server.MapPath(MyPath)
'.... some code
Catch ex As Exception
Finally
outputStream.Close()
End Try
End Function)
The PushStreamContent constructor definition is:
Public Sub New ( _
onStreamAvailable As Action(Of Stream, HttpContent, TransportContext) _
)
Related
I have been trying without success to return data from a an internally developed WebApi. The API uses Post requests to return data the body of the request containing complex criteria. I have previously had success using a Post Request to write data via the API, but I seem to have hit a brick wall in my efforts to return data. Here is the method I have been using:
Public Async Function Request(ByVal client As HttpClient, ByVal content As String) As Task
Dim buffer = System.Text.Encoding.UTF8.GetBytes(content)
Dim byteContent = New ByteArrayContent(buffer)
byteContent.Headers.ContentType = New MediaTypeHeaderValue("application/json")
Dim response = Await client.PostAsync(client.BaseAddress, byteContent)
Dim result As String = response.Content.ReadAsStringAsync().Result
_result = result
End Function
For conveninence the results are stored in a class variable, which enables me to test the routine using MSTest. The function should return a JSON array however it doesn't even return an error, rather it just returns Nothing.
Here is the associated TestMethod:
<TestMethod()> Public Sub ShouldSuccessfullyInterrodateTheSpenHapi()
Dim da = New SPEN.ODSDataPackagerService
Dim crit As New HttpCriterion
Dim stopped As Boolean
Dim jsonString As String = Nothing
crit.StartTime = "2022-11-03T00:00:00"
crit.EndTime = "2022-11-03T00:30:00"
crit.Interval = "30m"
crit.TagAttribute = "InstrumentTag"
crit.TagFilter = "confidential"
crit.Delimiter = "~"
crit.ServerName = "SPRODA"
'Deserialize object to JSON
jsonString = da.GetCriteriaJson(crit)
Try
da.InitialiseSettings()
da.Request(da.Clients(0), jsonString)
Debug.Print(da.Result)
Assert.IsTrue(True)
Catch ex As Exception
Assert.IsTrue(False)
End Try
End Sub
What am I missing guys?
EDIT:
Ok some further experimentation with the following code, I am at least now getting an error:
Public Async Function GetVoltagesFromPI(ByVal client As HttpClient, ByVal content As String) As Task(Of PIItemList)
Dim piItems As PIItemList = New PIItemList
Dim buffer = System.Text.Encoding.UTF8.GetBytes(content)
Dim byteContent = New ByteArrayContent(buffer)
byteContent.Headers.ContentType = New MediaTypeHeaderValue("application/json")
Try
Dim response As New HttpResponseMessage
response = Await client.PostAsync(client.BaseAddress, byteContent)
If response.IsSuccessStatusCode Then
_result = New PIItemList
_result = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result)
End If
Catch ex As Exception
Throw
Finally
End Try
End Function
The function errors at:
response = Await client.PostAsync(client.BaseAddress, byteContent)
with:
System.NullReferenceException: 'Object variable or With block
But really I am no further forward with regards a solution as far as I can make out the code should work, but there is obviously smething flawed in the code implementation.
Kind Regards
Paul.
Finally... Managed to get things working, here is the bare bones working code. Would appreciate any additional input though:
''' <summary>
''' Request pi data via the specified http endpoint (baseaddress)
''' </summary>
''' <param name="client"></param>
''' <param name="content"></param>
Public Function GetVoltagesFromPI(ByVal client As HttpClient, ByVal content As String) As Task(Of PIItemList)
Dim requestMsg As New HttpRequestMessage(HttpMethod.Post, client.BaseAddress)
Dim response As HttpResponseMessage
Dim interim As Object
Try
requestMsg.Content = New StringContent(content, Text.UTF8Encoding.Default, "application/json")
response = client.SendAsync(requestMsg).Result
If (response.StatusCode = System.Net.HttpStatusCode.OK) Then
_resultSet = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result)
End If
Catch ex As Exception
Throw
Finally
End Try
End Function
I am trying to parse through some xml files and return the results to a datagrid in UWA. The code builds fine but when running it returns
Unable to cast object of type 'System.Threading.Tasks.Task1[System.Collections.Generic.List1[Festplatten_Archiv_Client.Drive]]' to type 'System.Collections.IEnumerable'.
In my XAML.vb I am only calling the class to create the files and set the results as fileSource:
Public Sub New()
InitializeComponent()
dataGrid.ItemsSource = Drive.Drives
End Sub
Which works fine if I only add a sample Drive with
drivelist.Add(New Drive("Name",0, 0, 0), "location", "date"))
But as I want to parse through the XMLs, this is my code.
This is my drives class:
Public Shared Async Function Drives() As Task(Of List(Of Drive))
Dim drivelist As New List(Of Drive)
Dim folderpicked As StorageFolder
Try
folderpicked = Await StorageApplicationPermissions.FutureAccessList.GetItemAsync(ReadSetting("folderStorageToken"))
Catch ex As Exception
Debug.WriteLine("Fehler: " & ex.Message)
folderpicked = Nothing
End Try
Dim xmlfiles As List(Of StorageFile) = Await folderpicked.GetFilesAsync()
For Each file In xmlfiles
''Process files
Next
Return Await Task.Run(Function() drivelist)
End Function
It might be something with async programming, but I am very new to this. Thanks for any help!
You can make a blocking call to an Async routine from the ctor as follows:
Dim drivesResult = Drives().GetAwaiter().GetResult()
This is effectively forcing the routine to execute synchronously. If that's not what you want, then you'll need to explore a different alternative, e.g. the suggestion in the comments.
I get the following error Class 'PropGenie_WebService.Branch' cannot be indexed because it has no default property. And I am not sure why. I have googled but don't get a proper explanation or fix. C# help welcome.
My code in the branch.vb class:
Public Function Update() As Branch
Return Update(Me, Path) 'error at update.
End Function
And in my Base class (Resources.vb) I have:
Public Shared Function Update(Of T As {Resources, New})(resource As T, path As String) As T
Dim request = CreateRequest(path & "/{id}", Method.PATCH)
request.AddUrlSegment("id", resource.Id.ToString(CultureInfo.InvariantCulture))
request.AddBody(resource)
Dim Client = CreateClient()
Dim responce = Client.Execute(Of T)(request)
If responce.StatusCode <> HttpStatusCode.OK Then
Throw New InvalidOperationException("Update Failed" & Convert.ToString(responce.StatusCode))
End If
Return responce.Data
End Function
You need to specify the class in which the shared function is also, or it will try to use the Update function in the object you are in.
Public Function Update() As Branch
Return Resources.Update(Me, Path)
End Function
I am trying to write some code that will allow me to dynamically load DLLs into my application, depending on an application setting. The idea is that the database to be accessed is set in the application settings and then this loads the appropriate DLL and assigns it to an instance of an interface for my application to access.
This is my code at the moment:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim obj As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj, ICRDataLayer)
MsgBox(SQLDataSource.ModuleName & vbNewLine & SQLDataSource.ModuleDescription)
I have my interface (ICRDataLayer) and the SQLServer.dll contains an implementation of this interface. I just want to load the assembly and assign it to the SQLDataSource object.
The above code just doesn't work. There are no exceptions thrown, even the Msgbox doesn't appear.
I would've expected at least the messagebox appearing with nothing in it, but even this doesn't happen!
Is there a way to determine if the loaded assembly implements a specific interface. I tried the below but this also doesn't seem to do anything!
For Each loadedType As Type In ass.GetTypes
If GetType(ICRDataLayer).IsAssignableFrom(loadedType) Then
Dim obj1 As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj1, ICRDataLayer)
End If
Next
EDIT: New code from Vlad's examples:
Module CRDataLayerFactory
Sub New()
End Sub
' class name is a contract,
' should be the same for all plugins
Private Function Create() As ICRDataLayer
Return New SQLServer()
End Function
End Module
Above is Module in each DLL, converted from Vlad's C# example.
Below is my code to bring in the DLL:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t As Type = factory.GetType
Dim method As MethodInfo = t.GetMethod("Create")
Dim obj As Object = method.Invoke(factory, Nothing)
SQLDataSource = DirectCast(obj, ICRDataLayer)
EDIT: Implementation based on Paul Kohler's code
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
For Each ty As System.Type In s
If ty.Name.Contains("ICRDataLayer") Then
MsgBox(ty.Name)
plugin = DirectCast(Activator.CreateInstance(assemblyType), ICRDataLayer)
MessageBox.Show(plugin.ModuleName)
End If
Next
I get the following error with this code:
Unable to cast object of type 'SQLServer.CRDataSource.SQLServer' to type 'DynamicAssemblyLoading.ICRDataLayer'.
The actual DLL is in a different project called SQLServer in the same solution as my implementation code. CRDataSource is a namespace and SQLServer is the actual class name of the DLL.
The SQLServer class implements ICRDataLayer, so I don't understand why it wouldn't be able to cast it.
Is the naming significant here, I wouldn't have thought it would be.
Final Working code
Contents of PluginUtility:
enter code here Public Shared Function GetInstances1(Of Type)(ByVal baseDir As String, ByVal searchPattern As String) As System.Type()
Dim tmpInstances As New List(Of Type)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
Return s.ToArray()
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
End Function
Code to load the DLL:
enter code here
Dim basedir As String = "M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\"
Dim searchPattern As String = "*SQL*.dll"
Dim plugin As CRDataLayer.ICRDataLayer
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetExportedTypes
If assemblyType.GetInterface("CRDataLayer.ICRDataLayer") IsNot Nothing Then
plugin = DirectCast(Activator.CreateInstance(assemblyType), CRDataLayer.ICRDataLayer)
MessageBox.Show(plugin.ModuleDescription)
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
Catch ex As Exception
MsgBox(ex.Message)
Clipboard.SetText(ex.Message)
End Try
Version 2 - This sample loads up a DLL from it current directory.
There are 2 projects, 1 console application project and a "module" project (the module 'coppies' its DLL to the working directory of the console app).
The sample below simply demonstrates dynamically loading a DLL that implements an interface. The IModule interface just reports its name. PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll") will create an instance of any IModule instance found in a DLL within the current directory ending with ".Module.dll". It's a reflected VB.NET version straight out of Mini SQL Query.
With that in mind something like:
Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll")
Should satisfy your requirement. Then you just need to chose which one to execute!
The code:
In "VB.LoaderDemo Colsole App"
' IModule.vb
Public Interface IModule
Property ModuleName() As String
End Interface
' PlugInUtility.vb
Imports System.IO
Imports System.Reflection
Public Class PlugInUtility
Public Shared Function GetInstances(Of T)(ByVal baseDir As String, ByVal searchPattern As String) As T()
Dim tmpInstances As New List(Of T)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes()
If (Not assemblyType.GetInterface(GetType(T).FullName) Is Nothing) Then
tmpInstances.Add(DirectCast(Activator.CreateInstance(assemblyType), T))
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
Return tmpInstances.ToArray()
End Function
End Class
' MainModule.vb
Module MainModule
Sub Main()
Dim plugins As IModule() = PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")
Dim m As IModule
For Each m In plugins
Console.WriteLine(m.ModuleName)
Next
End Sub
End Module
In "Sample1 DLL" (references 'VB.LoaderDemo' for IModule)
Imports VB.LoaderDemo
Public Class MyModule1
Implements IModule
Dim _name As String
Public Sub New()
_name = "Sample 1, Module 1"
End Sub
Public Property ModuleName() As String Implements IModule.ModuleName
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
The output is:
> Sample 1, Module 1
Is the type ICRDataLayer defined in the DLL you are going to load? If so, you seem to already reference the DLL in your project settings.
You need to work with just reflection:
Dim obj As Object = ass.CreateInstance("ICRDataLayer", True)
Dim t as Type = obj.GetType()
Dim method as MethodInfo = t.GetMethod("DoSomething")
method.Invoke(obj, ...)
Edit: If ICRDataLayer is implemented in the application, and the plugin just implements the interface, you need the plugin to provide a factory for you: (sorry for C# code, I am not familiar with VB.NET's syntax)
// in each of plugins:
static class CRDataLayerFactory // class name is a contract,
{ // should be the same for all plugins
static ICRDataLayer Create()
{
return new CRDataLayerImplementation();
}
}
The application's code should look like this:
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t as Type = factory.GetType()
Dim method as MethodInfo = t.GetMethod("Create")
Dim obj as Object = method.Invoke(factory, null)
SQLDataSource = DirectCast(obj, ICRDataLayer)
A few things to look for in your code
Debug through and check that the
assembly is loaded correctly, in case
it fails due to dependency checking
Instead of using GetType, use GetExportedType so you have smaller subset to iterate through
The CreateInstance should use your loadedType rather than the interface (you cant create object from an interface)
Personally, I dont like naming my variable ass, I would shorten it to assem instead :)
I am trying to return a binary from the DB using linq for display in the browser. The method below using ado.net works but I am trying to ypgrade to linq but the linq version returned the error.
Public Sub ProcessRequest(ByVal context As System.Web.HttpContext)
Dim imageId As String = context.Request.QueryString("id")
Dim ret As DataTable = Nothing
ret = DPGetImageData.GetImageById(Convert.ToInt64(imageId))
For Each dt As DataRow In ret.Rows
For Each c As DataColumn In ret.Columns
If Not (dt(c) Is Nothing) Then
context.Response.Clear()
context.Response.BufferOutput = False
context.Response.OutputStream.Write(CType(dt.Table.Rows(0).Item("imageData"), Byte()), 0, CInt(dt.Table.Rows(0).Item("imageSize")))
context.Response.End()
End If
Next
Next
End Sub
Working Linq Version:
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
If Not String.IsNullOrEmpty(Current.Request.QueryString("Id")) Then
Dim imageId = HttpContext.Current.Request.QueryString("Id")
Dim result = repository.FindById(Convert.ToInt64(imageId)).imageData.ToArray
HttpContext.Current.Response.BinaryWrite(result)
context.Response.End()
End If
End Sub
You should be able to just call Response.BinaryWrite(result.ToArray()). Notice the parantheses, ToArray is a method call.
You should not need the cast as Binary.ToArray, returns a byte array already.
An alternative is to use a byte array directly. You can modify it in the designer.
UPDATE:
I am not fully sure, but it could be that your DataContext has been disposed already. I think the Binary type uses deferred loading.
If you add a stacktrace, we could see if that is the case.