Right now I'm able to establish a connection within my class by calling it in each method by doing the following.
Dim sConnectionString As String = ConfigurationManager.AppSettings("Blah")
'Establish connection with db
Dim cnSqlConnection1 As New SqlConnection(sConnectionString)
Only problem is that I have to call it in each method. I was told that it was better to create a constructor for the class nad have the connection string it uses passed into the constructor.
Here's my attempt but can't seem to figure out since I'm still unable to reach it in the method.
Public Sub New(ByVal sConnectionString As String)
sConnectionString = ConfigurationManager.AppSettings("Blah")
End Sub
What is the best way to do it? Thanks in advance.
You should store the passed connectionstring in a global variable available in all of your class methods
Public Clas MyClass
Private String gs_conString
Public Sub New(ByVal sConnectionString As String)
gs_conString = sConnectionString
End Sub
Public Sub AMethod()
'Establish connection with db
Dim cnSqlConnection1 As New SqlConnection(gs_conString)
.....
End Sub
.....
End Class
Of course this means that every time you create an instance of this class you need to pass the connection string to your constructor
Dim cl As MyClass = new MyClass(ConfigurationManager.AppSettings("Blah"))
So it is probably better to use the constructor to extract the connection string automatically everytime you create an instance
Private String gs_conString
Public Sub New()
gs_conString = ConfigurationManager.AppSettings("Blah")
End Sub
Go with the first option, putting the connection string in the constructor. You don't want your class to depend directly on <appSettings>.
Your class's interface should indicate what dependencies it has. When you put the connection string in the constructor, the class says, "Hey, I need a connection string!"
If the class calls <appSettings> then a user of the class has no way of knowing that the class expects to find a connection string there unless they open your code and read it. If they don't know that the connection string belongs there then they'll get a null reference exception with no explanation.
That raises the question - whatever class creates your class, where does it get the connection string so it can pass it to the constructor? Dependency injection is the answer. It enables you to write classes that way then "wire it up" so that the correct arguments get passed to your constructors.
Related
I'm trying to set up some WCF services that are connected and pass a custom HTTP header from service to service.
That is my client call ServiceX, which calls ServiceY, which writes to the DB.
They are originally called from a Silverlight 5 client in some cases, other cases from an ASP.NET web app.
I implemented IClientMessageInspector and IDispatchMessageInspector to pass the header from service to service, and in the DispatchMessageInspector I wrote the header to an implementation of IExtension(Of OperationContext) (see below).
However, I wanted this data only to exist for the duration of the call, but it seems to be sticking around in the services under certain circumstances as I keep seeing the same header data repeated in different calls.
Ultimately, I want to be able to pass a custom header to a WCF service, persist it only while that call exists, send it to the next service in the header, and wipe out that service instance. Am I wrong in my thinking that using a PerCall WCF service setup and OperationContext is the right way to do that?
Here is my implementation of IExtension(Of OperationContext). The auditTransactionId is the thing I want to pass in the service. As well, the Current() property is where I keep seeing existing data:
Imports System.ServiceModel
Public Class CustomOperationContextExtension
Implements IExtension(Of OperationContext)
Private ReadOnly m_items As IDictionary(Of String, Object)
Private m_auditTransactionId As String
Private Sub New()
m_items = New Dictionary(Of String, Object)()
End Sub
Public ReadOnly Property Items() As IDictionary(Of String, Object)
Get
Return m_items
End Get
End Property
Public Property AuditTransactionId() As String
Get
Return m_auditTransactionId
End Get
Set(value As String)
m_auditTransactionId = value
End Set
End Property
Public Shared ReadOnly Property Current() As CustomOperationContextExtension
Get
If (OperationContext.Current IsNot Nothing) Then
Dim context As CustomOperationContextExtension = OperationContext.Current.Extensions.Find(Of CustomOperationContextExtension)()
If context Is Nothing Then
context = New CustomOperationContextExtension()
OperationContext.Current.Extensions.Add(context)
End If
Return context
End If
Return Nothing
End Get
End Property
Public Sub Attach(owner As OperationContext) Implements IExtension(Of System.ServiceModel.OperationContext).Attach
End Sub
Public Sub Detach(owner As OperationContext) Implements IExtension(Of System.ServiceModel.OperationContext).Detach
End Sub
End Class
EDIT:
When I say that data is sticking around, I mean that when I call Current in a new service call I expect the Extensions list to be empty (in the code below in the Current() property), but there is always an existing instance of CustomOperationContextExtension there already that is left over fro a previous call. I'm not sure under which circumstances this happens.
I have database table which has a field called "datatype". It will hold a particular datatype, such as string, integer, classname, etc....
How can I dim a variable using whatever is stored in this field? For example, if the field says "string" I want to effectively say "Dim MyVar as string", but fill in the "string" portion with what's stored in the table. Hope that makes sense. Thanks!
You can create instances from the name of the class. Note, you can't ask for "MyClass1" without the namespace before it. There are some options for getting from "MyClass1" to "Namespace.MyClass1" such as a Dictionary or even putting the full type name in your database.
Module Module1
Sub Main()
' compiler knows mc1 is a IMyClasses
Dim mc1 = CType(getInstanceFromTypeName("ConsoleApplication1.MyClass1"), IMyClasses)
' compiler doesn't know, mc2 is an object
Dim mc2 = getInstanceFromTypeName("ConsoleApplication1.MyClass2")
mc1.Foo()
mc2.foo()
End Sub
Private Function getInstanceFromTypeName(typeName As String) As Object
Dim o As Object
Try
o = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(typeName)
Catch ex As Exception
o = Nothing
End Try
Return o
End Function
End Module
Public Class MyClass1
Implements IMyClasses
Public Sub Foo() Implements IMyClasses.Foo
Debug.Print("MyClass1")
End Sub
End Class
Public Class MyClass2
Implements IMyClasses
Public Sub Foo() Implements IMyClasses.Foo
Debug.Print("MyClass2")
End Sub
End Class
Public Interface IMyClasses
Sub Foo()
End Interface
mc1.Foo() works because mc1 is declared as an IMyClasses, and IMyClasses defines this subroutine. The compiler knows that IMyClasses defines Foo.
mc2.foo() doesn't work with Option Strict On because Foo() is not a member of Object. With O.S.On, the compiler must be able to resolve all function calls at compile time. It works with Option Strict Off however, as O.S.Off allows function calls on Object, but can potentially be dangerous because O.S.Off also allows mc2.asdf(), for example.
Other resources:
Using System.Reflection
Using System.Activator
Im trying to set a connection string for a my data access layer which is using a value from my Web.Config file from another project.
I create a new class library and add a dbml (Linq to SQL) file and drag a table.
I then add this code to the code file
Partial Public Class MyDataContext
Public Sub New()
MyBase.New(ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString, mappingSource)
OnCreated()
End Sub
End Class
This gave me an error "'Public Sub New()' has multiple definitions with identical signatures."
I understood what the error means so did a quick search on a way around it as when i recompiled the project the same problem remained. The way to approach this was/is to override the OnCreated method so i changed the code to:
Private Sub OnCreated()
Me.New(ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString, mappingSource)
End Sub
This gave the error "Constructor call is valid only as the first statement in an instance constructor" but not a lot of ways to overcome it (well i see a few C# examples but im sure im an converting it to the correct VB .Net code)
After further research no matter what i do, i dont seem to be able to set the connection string in my DAL which should be using the Web.Config connection string value from another project.
What could i be doing wrong?
OK, In C# first:
partial class MyDataContext
{
public static MyDataContext Create()
{
return new MyDataContext(ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString,
mappingSource);
}
// etc
}
Now, my attempt to translate that in VisualBasic.NET
Partial Public Class MyDataContext
Public Shared Function Create() as MyDataContext
return New MyDataContext (ConfigurationManager.ConnectionStrings("WebConnectionString").ConnectionString,
mappingSource)
End Function
End Class
Called via:
Dim db as MyDataContext = MyDataContext.Create()
Using VB.NET, I have ths class
Public Class MyCollectionClass
Implements IEnumerable(Of MyClass)
Public Property MadeThisClassCuzINeedToSetThis() As String
' code here
End Class
I want to do this, but get an exception saying I can't do this cast.
Dim objColl As MyCollectionClass
objColl = CType(IEnumerable(Of MyClass), MyCollectionClass)
Can anyone tell me how to get this to work. Thanks.
See this VB.NET/C# casting cheat sheet or the documentation on CType. The major problem is that the first parameter should be the instance to convert, not its type. This should work:
Dim myEnumerable As IEnumerable(Of MyObjectClass) = New MyCollectionClass()
Dim objColl = CType(myEnumerable, MyCollectionClass)
' objColl's type is inferred As MyCollectionClass
(note that as MyClass is a keyword, and I assume you actually have a different class name there, I changed it to MyObjectClass in my example)
I'm writing a WCF WebMethod to upload files to, of which I taken snippets from around the web. The WCF interface looks like this:
<ServiceContract()>
Public Interface ITransferService
<OperationContract()>
Sub UploadFile(ByVal request As RemoteFileInfo)
End Interface
<MessageContract()>
Public Class RemoteFileInfo
Implements IDisposable
<MessageHeader(MustUnderstand:=True)>
Public FileName As String
<MessageHeader(MustUnderstand:=True)>
Public Length As Long
<MessageBodyMember(Order:=1)>
Public FileByteStream As System.IO.Stream
Public Sub Dispose() Implements IDisposable.Dispose
If FileByteStream IsNot Nothing Then
FileByteStream.Close()
FileByteStream = Nothing
End If
End Sub
End Class
Within ASP.NET, when the web method is consumed, for some reason it only works when the interface is used as part of the instantiation of RemoteFileInfo:
Protected Sub btn_Click(sender As Object, e As EventArgs) Handles btn.Click
If fu.HasFile Then
Dim fi As New System.IO.FileInfo(fu.PostedFile.FileName)
' this is the line in question --------------
Dim cu As ServiceReference1.ITransferService = New ServiceReference1.TransferServiceClient()
' -------------------------------------------
Dim uri As New ServiceReference1.RemoteFileInfo()
Using stream As New System.IO.FileStream(fu.PostedFile.FileName, IO.FileMode.Open, IO.FileAccess.Read)
uri.FileName = fu.FileName
uri.Length = fi.Length
uri.FileByteStream = stream
cu.UploadFile(uri)
End Using
End If
End Sub
Can anyone advise why it is not possible to create an instance of TransferService using the following approach:
Dim cu As New ServiceReference1.TransferServiceClient()
If I try the above, it breaks this line:
cu.UploadFile(uri)
...and UploadFile must be called with three parameters (FileName, Length, FileByteStream) even there is no method that uses this signature.
Why is the Interface required when creating an instance of this class please?
When you create the proxy for your service with the "Add Service Reference" dialog, by default the proxy creation code will "unwrap" message contracts, like the one you have. If you want the message contract to appear as you defined on the server side on your proxy, you need to select the "Advanced" tab, and check the "Always generate message contracts" option. With that you'll get the message contract in your client as well.
The issue is that when a MessageContract is encountered as a parameter, the WCF client generation assumes by default that you want to implement a messaging-style interface, and provides the discrete properties from the message contract as part of the client-side interface.
The Using Messaging Contracts article in MSDN contains a very detailed description of what can be done with a messaging contract and I suspect that Microsoft chose this default behavior because of some of the "games" that you can play with the messages.
However, if you examine the code generated for your UploadFile on the client side, there are some interesting tidbits that help to explain what is going on.
The first is the comments for the UploadFile method in the interface:
'CODEGEN: Generating message contract since the operation UploadFile is neither RPC nor document wrapped.
...
Function UploadFile(ByVal request As ServiceReference1.RemoteFileInfo) As ServiceReference1.UploadFileResponse
This implies that the contract would have been generated differently if the message contract had a different implementation.
The second is that you will see that there is nothing special about the code that is used to actually make the service call:
Public Sub UploadFile(ByVal FileName As String, ByVal Length As Long, ByVal FileByteStream As System.IO.Stream)
Dim inValue As ServiceReference1.RemoteFileInfo = New ServiceReference1.RemoteFileInfo()
inValue.FileName = FileName
inValue.Length = Length
inValue.FileByteStream = FileByteStream
Dim retVal As ServiceReference1.UploadFileResponse = CType(Me,ServiceReference1.ITransferService).UploadFile(inValue)
End Sub
So in this case, your code is doing exactly what the generated code does. However, if the MessageContract were more complex, I suspect that this would no longer be the case.
So, for your question:
Can anyone advise why it is not possible to create an instance of
TransferService using the following approach...
There is no reason not to take this approach as long as you verify that the implementation of the method call is functionality equivalent to your code.
There are a couple of options for changing the default generation of the method in the client:
1) Remove the MessageContract attribute from the RemoteFileInfo class.
2) Although it seems to be counter-intuitive, you can check the Always generate message contracts checkbox in the Configure Service Reference Dialog Box.