I am trying to make 2 variables available throughout my site. I am parsing the URL in order to retreive both of them.
This code on the page itself works fine.
Dim countryLanguage As String
countryLanguage = (Request.ServerVariables("URL"))
Dim langVar = (Mid(countryLanguage, 2, 2))
Dim countryVar = (Mid(countryLanguage, 5, 2))
I have created a class file the code for which is below. With this I get a complilation error (BC30451: Name 'Request' is not declared.).
Public Class url_parser
Public Shared Function urlVars(ByVal langVar, ByVal countryVar) As String
Dim countryLanguage As String
countryLanguage = (Request.ServerVariables("URL"))
Dim langVar = (Mid(countryLanguage, 2, 2))
Dim countryVar = (Mid(countryLanguage, 5, 2))
End Function
End Class
Thanks
System.Web.HttpContext.Current.Request
Your page would already be importing System.Web and already references HttpContext.Current for you internally. In an outside class you have to make sure you import the namespace manually and reference the current context.
On a side note, coupling non-page classes to page lifecycle objects (such as HttpContext) is considered bad design. But for getting started it's fine just to make it work. However, I recommend reading up on the MVC pattern and Separation Of Concerns (and Dependency Injection). If you're a beginner programmer it may be a bit advanced, but if you want to program for a long time consider it a worthy goal to set your sights on :)
You can use System.Web.HttpContext.Current.Request to get the request object for the current thread.
A better way to get your country and language folders is to use Request.Url.Segments.
Public Class url_parser
Public Shared Function urlLanguage() As String
Dim Request = Web.HttpContext.Current.Request
Return Request.Url.Segments(1).TrimEnd("/"c)
End Function
Public Shared Function urlCountry() As String
Dim Request = Web.HttpContext.Current.Request
Return Request.Url.Segments(2).TrimEnd("/"c)
End Function
End Class
Access these static function this way.
Dim MyLanguage = url_parser.urlLanguage
Dim MyCountry = url_parser.urlCountry
In this example, if the Url is "/en/us/" then...
Segment(0) is "/"
Segment(1) is "en/"
Segment(2) is "us/"
The Request object is generally not available within a simple stand-alone class.
I would recommend you pass the string value of countryLanguage as an additional variable from the call within your page class.
If you really must access the Reqeust object from within the class (not recoemmended) use:
HttpContext.Current.Request
Related
I am trying to find a particular user from a mongodb collection that matches the given id. Folliwng is my VB.Net code. However, I keep getting the error 'Public member 'Find' on type 'MongoCollectionImpl(Of BsonDocument)' not found.'
Public Function GetCollectionByName(ByVal collectionName As String)
Dim db As IMongoDatabase = DBcontext()
Dim collection As IMongoCollection(Of BsonDocument)
collection = db.GetCollection(Of BsonDocument)(collectionName)
Return collection
End Function
Public Function GetUser(ByVal id As String)
Dim filter = Builders(Of BsonDocument).Filter.Eq(Of String)("ID", id)
Dim collection = GetCollectionByName("Users")
Dim list = collection.Find(filter).ToList()`<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ERROR here
Return list
End Function
Firstly, you must have Option Strict Off for that code to even compile. That's bad. You should immediately turn Option Strict On in the project properties and address all the issues it raises. One of those will be the fact that your GetCollectionByName has no return type declared. That means that here:
Dim list = collection.Find(filter).ToList()
that collection variable is implicitly type Object and you are relying on late binding when calling that Find method because the Object class has no such method. As a result, you get no help from Intellisense and Intellisense would have told you what members were and were not available if you were doing this properly.
Regardless, you still could have made it work if you had actually read the documentation for the types you're using to see what members they have. Here is the documentation for the interface you're using in that GetCollectionByName method and, I don't know about you but I don't see any Find method listed there. There is a FindSync method, so maybe that's what you actually want. If you had Option Strict On and used proper types every where, Intellisense would have shown you that.
You should also turn Option Strict On in the IDE options, so that it is On for all future projects.
I had a look at some documentation for the MongoCollectionImpl for Java and there appears to be a find method there but that doesn't necessarily mean that the same method is available in .NET and you aren't working directly with that class anyway. You are working with the IMongoCollection so you should only be working with members of that interface. Basically, your code would need to look more like the below with Option Strict On:
Public Function GetCollectionByName(ByVal collectionName As String) As IMongoCollection(Of BsonDocument)
Dim db As IMongoDatabase = DBcontext()
Dim collection As IMongoCollection(Of BsonDocument)
collection = db.GetCollection(Of BsonDocument)(collectionName)
Return collection
End Function
Public Function GetUser(ByVal id As String) As List(Of BsonDocument)
Dim filter = Builders(Of BsonDocument).Filter.Eq(Of String)("ID", id)
Dim collection = GetCollectionByName("Users")
Dim list = collection.FindSync(filter).ToList()
Return list
End Function
You may want to declare the GetUser method as type IList(Of BsonDocument) if you want to work with interfaces. You probably ought to rename that method or change the implementation too. If a method is returning a list then the name should not indicate that it returns a single item.
I've heard advice to change from User Defined Type (UDT) to a regular Class in order to overcome the limitations of UDT, such as not being able to use For Each with a UDT.
I've also heard advice to change from a regular Class to UDT to overcome the Class limitation where you can't pass things BYREF, like...
'Function:
Public Function RemoveArticle (ByRef strMovieTitle As String)
'Expected input is like "Terminator, The"
strMovieTitle = Left(... 'removes the article.
End Function
That works fine for this call:
Dim strMovieTitle As String
strMovieTitle = "Terminator, The"
RemoveArticle strMovieTitle
But not this call:
Dim objMovie As MovieClass
objMovie.strMovieTitle = "Terminator, The"
objMovie.strMovieGenre = "Sci-Fi"
InvertArticle objMovie.strMovieTitle
Even though MovieClass defines
strMovieTitle As String
I can't go changing RemoveArticle (and every simple little function like it) to take a MovieClass parameter instead of a String parameter because there are other UDTs or Classes and String Variables that also need to use RemoveArticle.
What do I do if I need to use For Each and I also need to pass ByRef?
Is there a way a Class can work around the parameter problem?
(Using Excel 2010.)
Now I have understood your concern.
You simply can't take that approach to meet your goal. As Tim Williams has commented in your question, your best bet would be something like this:
Dim objMovie As MovieClass
Dim strMovieTitle As String
strMovieTitle = "Terminator, The"
objMovie.strMovieTitle = InvertArticle(strMovieTitle)
However, I see that this still does not satisfy your need.
My suggestion is as follows:
make your object internal, target properties Private and expose them with Property Let and Property Get. This way you can do the modifications you want to the properties either on set or on get (from within the class... rather than fixing things from outside the class).
Aside note, in regards to create a helper class (as someone has recommended to you): you could join into one class all those functions you use widely, such as RemoveArticle or InvertArticle. However, it requires to create an instance object every time you want to use them and, therefore, does not combine well with the recommendation I am giving to you (if you want just to simplify code). So having them in a Module as you do now is fine. Just to clarify: those recommendations they gave to you are unrelated to your question here.
Example A: on set
In you class MovieClass, rename first all the instances of strMovieTitle to pStrMovieTitle and add this to your code:
Private pStrMovieTitle As String
Public Property Let strMovieTitle (strIn As String)
pStrMovieTitle = InvertArticle(strIn)
End Property
Public Property Get strMovieTitle As String
strMovieTitle = pStrMovieTitle
End Property
The usage would be something like this:
Dim objMovie As MovieClass
objMovie.strMovieTitle = "Terminator, The" ' the article position gets rectified on assignation
objMovie.strMovieGenre = "Sci-Fi"
'InvertArticle objMovie.strMovieTitle ' => you don't need to do this call
Example B: on get
To keep your original string as it comes, and do apply your helpers when you get the property value. That way you always preserve the original string. However, this approach will need more rework and it's only worthy in cases where you have lots of ways to use that String in different parts of your code.
Private pStrMovieTitleSource As String
Public Property Let strMovieTitle (strIn As String)
pStrMovieTitleSource = Trim(strIn)
End Property
Public Property Get strMovieTitleSource () As String
strMovieTitleSource = pStrMovieTitleSource
End Property
Public Property Get strMovieTitleRoot () As String
strMovieTitleRoot = RemoveArticle(pStrMovieTitleSource)
End Property
Public Property Get strMovieTitle () As String
strMovieTitle = InvertArticle(pStrMovieTitleSource)
End Property
Hope it helps
To avoid getting into the weeds on my particular program, let me just create a simplified case.
I have a generic class that should work on a variety of objects. Each of those objects must implement a certain interface.
What I WANT to say is something like:
Public Interface GenThing
Shared Function thing_name() As String ' This doesn't work! Can't be shared!
Sub FillOne(row As DataRow)
End Interface
public class Thing1
implements GenThing
public shared function thing_name() as string implements GenThing.thing_name
return "thing number one"
end function
public sub FillOne(row as DataRow) implements GenThing.MakeOne
... bunch of work ...
end sub
end class
public class ThingUtil(of T as {GenThing,New})
public function GetList(id as integer) as List(of T)
dim name=T.thing_name() ' This doesn't work!
dim ds as DataSet=GetData(name,id) ' bunch of work here that's the whole point of the class but not relevant to the question
dim my_list = new List(of T)
for each row as DataRow in ds.tables(0).rows
dim my_t = new T()
my_t.FillOne(row)
my_list.add(my_t)
next
return my_list
end function
end class
Do you get my problem? I need every class that implements the interface to have a function that returns a "name" that is used to get the data that is needed to create an instance of the object. But I need to know this name BEFORE I create the instance, because I need it to be able to create the instance. But VB doesn't allow an interface to have a shared function, so what I want to write doesn't work.
So what I've done is this:
I make thing_name not shared.
Then instead of simply "dim name=T.thing_name()", I write
dim dummy = new T()
dim name = dummy.thing_name()
Okay, it works, but it seems really ugly. I create an instance of the object, with all the overhead that that involves, just to get a piece of constant text.
Is there a better way? Or am I making a big deal out of nothing?
Update
I see that two people voted to close this question on the grounds that it is the same as "Why can't we have shared functions in an interface?"
I am not asking why I can't have a shared. I am saying, GIVEN that I can't, how do I solve this particular problem?
There's no really simple way of fixing this, no.
Depending on what thing_name does, however, you might approach things in a different way. If each implementation just returns a constant value, then it's effectively metadata about the class - and could be described in an attribute instead, which can be fetched at execution time. (See Type.GetCustomAttributes.) Unfortunately you can't then enforce all types implementing the interface to be decorated with the attribute - but you could write a unit test to check this pretty easily.
If thing_name needs to really do work at execution time, that's tougher. You could potentially look for a well-known shared method name instead and execute that via reflection (and again have unit tests to check that it's implemented properly).
I realize this is from a few years ago, but running into a similar problem, I wanted to offer a different solution. Pass a delegate as parameter to the ThingUtil constructor. You avoid having to put a shared method in an interface, and the constructor will force you to include the parameter at compile time.
You can add more delegates if needed, or to make it even simpler in this case, just pass name as a string instead of get_name as a delegate.
Define the delegate in the interface:
Public Interface GenThing
Delegate Function ThingNameDelegate() As String
Sub FillOne(row As DataRow)
End Interface
Public Class Thing1
Implements GenThing
Public Shared Function thing_name() As String 'name this whatever you want
Return "thing number one"
End Function
Public Sub FillOne(row As DataRow) Implements GenThing.FillOne
'do stuff
End Sub
End Class
In ThingUtil, add a member to store the delegate, a constructor parameter to to accept, and call it with .Invoke():
Public Class ThingUtil(Of T As {GenThing, New})
Private m_thing_name As GenThing.ThingNameDelegate
Public Sub New(thing_name As GenThing.ThingNameDelegate)
m_thing_name = thing_name
End Sub
Public Function GetList(id As Integer) As List(Of T)
Dim name = m_thing_name.Invoke()
Dim ds As DataSet = GetData(name, id) ' bunch of work here that's the whole point of the class but not relevant to the question
Dim my_list = New List(Of T)
For Each row As DataRow In ds.Tables(0).Rows
Dim my_t = New T()
my_t.FillOne(row)
my_list.Add(my_t)
Next
Return my_list
End Function
End Class
Finally, use it like this:
Dim tu as new ThingUtil(Of Thing1)(AddressOf Thing1.get_name)
tu.GetList(1)
I have an assembly in VB .NET 2.0 that I am trying to use to call a webservice.
This will be COM visible, and return the results to Access in VBA.
The .NET Assembly passes all tests and executes perfectly.
I was experiencing "Object does not support this property or method" errors when calling the methods from VBA.
I broke it down to a certain object that was being returned and added some test methods to the .NET DLL.
There is a "Patient" object I want to return.
It looks like this (made it very very simple to test it):
Option Strict On
Option Explicit On
<ComClass(Patient.ClassId, Patient.InterfaceId, Patient.EventsId)> _
Public Class Patient
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "672dfbd9-8f3a-4ba2-a33d-89fef868f2b9"
Public Const InterfaceId As String = "74a9c54c-4427-4d31-8220-3258ecda345d"
Public Const EventsId As String = "dc25515e-1bb7-4a66-97d5-270c00d792a9"
#End Region
Public Sub New()
MyBase.New()
End Sub
Public Property StorePatientID() As Integer
Get
Return m_StorePatientID
End Get
Set(ByVal value As Integer)
m_StorePatientID = value
End Set
End Property
Private m_StorePatientID As Integer
End Class
So about as simple as an object can be really.
I have a method that just returns a dummy record, just to test it:
Public Function GetPatientTest() As Patient
Dim patient As New Patient
patient.StorePatientID = 99
Return patient
End Function
This fails with the afformentioned error.
HOWEVER,
This method succeeds!
Public Function GetPatientArrayTest() As Patient()
Dim strings As New List(Of Patient)
Dim patient As New Patient
patient.StorePatientID = 99
strings.Add(patient)
Return strings.ToArray
End Function
The DLL is made com visible through "Properties" page.
Builds to project/bin/debug, always do a rebuild.
Always seems to be updated with new methods etc when I look at it in VBA so don't think it's looking at an old version.
Obviously no funny dependencies with these methods.
Really really struggling with this.
EDIT:
Update 16/03/2011 - Added VBA script
Public Function FindPatientsTest(ByVal surname As String, ByVal surnameBeginsWith As Boolean, ByVal forename As String, ByVal forenameBeginsWith As Boolean, ByVal dateOfBirth As String)
Dim token As String
token = Login()
Dim patient As SCIStoreWS60.patient
Set patient = New SCIStoreWS60.patient
'// This doesn't work.
'// When adding a "Watch" to the function, I can see it returns an "Object/Patient" and is the correct results
'// When adding a "Watch" to the variable "patient" I can see it is a "Patient/Patient"
patient = sciStore.GetPatientTest()
'// This works fine
Dim something As Variant
something = sciStore.GetPatientArrayTest()
End Function
Update 16/03/2011 5 minutes later - Chastising myself
Sorry, I just worked it out.
I need to "Set" the patient variable.
Set patient = sciStore.GetPatientTest()
Why didn't I need to do this for the "something" variant?
So, yes, you need to Set object references, but not arrays.
I'm currently working in .NET 2.0 Visual Basic. The current project is an Active Directory Wrapper class library within which I have a Searcher(Of T) generic class that I wish to use to search the underlying directory for objects.
In this Searcher(Of T) class I have the following methods:
Private Function GetResults() As CustomSet(Of T)
Public Function ToList() As CustomSet(Of T)
Public Function Find(ByVal ParamArray filter() As Object) As CustomSet(Of T)
// And some other functions here...
The one that interests me the most is the Find() method to which I can pass property and values and would like to parse my LDAP query from this filter() ParamArray parameter. Actually, all I can figure out is this:
Public Sub SomeSub()
Dim groupSearcher As Searcher(Of Group) = New Searcher(Of Group)()
Dim groupsSet as CustomSet(Of Group) = groupSearcher.Find("Name=someName", "Description=someDescription")
// Working with the result here...
End Sub
But what I want to be able to offer to my users is this:
Public Sub SomeSub()
Dim groupSearcher As Searcher(Of Group) = New Searcher(Of Group)()
Dim groupsSet As CustomSet(Of Groupe) = groupSearcher.Find(Name = "someName", Guid = someGuid, Description = "someDescription")
// And work with the result here...
End Sub
In short, I want to offer some kind of Expression feature to my users, unless it is too much work, as this project is not the most important one and I don't have like 2 years to develop it. I think that the better thing I should do is to write something like CustomExpression that could be passed in parameters to some functions or subs.
Thanks for any suggestions that might bring me to my goal!
Interesting question. This is a language dependent feature, so I don't see this happening without some clever trickery of the IDE/compiler.
You could however have optional overloads on your Find method (vb.net is good for this), then make the search string manually to obtain the result.
Finally you could make use of lambda functions, but only in .net 3.5 and above. Even still, it would require your searcher to expose a preliminary set of data so you can recover the expression tree and build up the find string.
UPDATE
I've just been playing around with Reflection to see if I can retrieve the parameters passed, and build up a string dynamically depending on if they exist. This doesn't appear to be possible, due to the fact that compiled code doesn't reference the names.
This code just used was:
'-- Get all the "parameters"
Dim m As MethodInfo = GetType(Finder).GetMethod("Find")
Dim params() As ParameterInfo = m.GetParameters()
'-- We now have a reference to the parameter names, like Name and Description
Hmm. http://channel9.msdn.com/forums/TechOff/259443-Using-SystemReflection-to-obtain-parameter-values-dynamically/
Annoyingly it's not (easily) possible to recover the values sent, so we'll have to stick with building up the string in a non-dynamic fashion.
A simple optional method would look like:
Public Sub Find( _
Optional ByVal Name As String = "", _
Optional ByVal Description As String = "")
Dim query As String = String.Empty
If Not String.IsNullOrEmpty(Name) Then
query &= "Name=" & Name
'-- ..... more go here with your string seperater.
End If
End Sub