I've added a reference (NuGet package) as described here, but LINQPad isn't recognizing it.
Here's my query:
Sub Main
Dim iImportList As New List(Of Integer)
Dim oSearch As New List(Of Match)
For i = 1 To 226
iImportList.Add(i)
Next
Using oDb As Db.Context = Db.Context.Create
oSearch.Add(From A In oDb.Applicants
Join C In oDb.Customers On
A.FirstName Equals C.FirstName And
A.LastName Equals C.LastName And
A.Ssn Equals C.Ssn And
A.Dob Equals C.Dob
Where
C.TotalBalance > 0 AndAlso
A.Aln.StartsWith(DateTime.Now.Year) AndAlso
iImportList.Contains(C.ImportId)
Select
New Match With {
.ApplicantId = A.ApplicantId,
.CustomerId = C.CustomerId,
.MatchLevel = Db.Match.MatchLevels.FirstLastSsnDob
})
End Using
End Sub
Class Match
Public Property ApplicantId As Integer
Public Property CustomerId As Integer
Public Property MatchLevel As Db.Match.MatchLevels
End Class
...and here's the result:
As we can see, LINQPad is ignoring my SQLCE reference and is using the SQL reference instead. (The System.Data reference is apparently internal to LINQPad; I didn't add it.)
How can I get LINQPad to do the opposite—ignore the SQL reference and use the SQLCE reference as intended?
Just use the EF DbContext connection dialog to hook up your DbContext, works like a charm.
It sounds like a runtime assembly resolution problem - something is expecting System.Data.SqlServerCe.dll to be in the output folder.
Press F4 for query properties, click the Advanced tab, and select the option to copy all non-framework references to a single folder. That will ensure that the runtime assemblies will be discoverable.
Related
I have been trying to clean up my code a bit and make it more similar to the Excel object model, and I was wondering if it is possible to create a "loopable" container class in VBA, e.g. similar to how you can do:
Dim Sheet As Worksheet
For Each Sheet In ThisWorkbook.Worksheets
' ...
Next Sheet
I want this functionality for my own container.
Say I create my own class called Container which contains items of some class ItemType (this can simply be an empty class for this example):
' Class Container
' The container contains items of a class I will call ItemType
Private Type MContainer
Items As Collection ' Could also be implemented in terms of an array
End Type
Private This As MContainer
Public Property Get Item(ByVal Index As Long) As ItemType
Attribute Item.VB_UserMemId = 0 'Makes it so I can access elements like a Collection
Set Item = This.Items(Index)
End Property
Public Function Add() As ItemType
This.Items.Add
Set Add = This.Items(This.Items.Count)
End Function
Private Sub Class_Initialize()
Set This.Items = New Collection
End Sub
I then want to loop through the items in my container with the For Each..., but this doesn't work. See the following example for how I ideally want it to work:
Public Sub MyMethod()
Dim Stuff As New Container
Stuff.Add
Dim Element As ItemType
For Each Element In Stuff ' <- This will not work
' Do something
Next Element
End Sub
The final For loop is what I am looking at making work. Is this possible? Basically the issue is that I can't call For Each on my Container class similar to how you can with e.g. the Excel.Sheets class. Is this possible to achieve in VBA?
For Each iteration requires a special member attribute value to work, and a NewEnum property or function returning an IUnknown.
Every collection class that can be iterated with a For Each loop has a hidden [_NewEnum] member (the square brackets are required for accessing the hidden member, since the underscore prefix is illegal for an identifier in VBA.
Tweaking module and member attributes isn't possible to do directly in the VBE, so you need to remove/export the module, modify it in e.g. Notepad++, save the changes, then re-import it into your project.
Or, have Rubberduck (disclaimer: I contribute to this open-source project) do it for you, using annotations (aka "magic comments"):
'#Enumerator
'#Description("Gets an enumerator that iterates through the internal object collection.")
Public Property Get NewEnum() As IUnknown
Set NewEnum = this.Items.[_NewEnum]
End Function
'#DefaultMember
'#Description("Gets/sets the element at the specified index.")
Public Property Get Item(ByVal index As Long) As ItemType
Set Item = this.Items(index)
End Property
Then parse the project (Ctrl+`) and bring up the Inspection Results toolwindow (Ctrl+Shift+i) - there should be a number of "Missing Attribute" results under "Rubberduck Opportunities":
Click "Fix all occurrences in module" in the bottom pane, to synchronize the hidden attributes with the annotation comments.
If you have "Missing Annotation" results, Rubberduck has determined that a module/member has a non-default value for a given attribute, and is able to similarly add an annotation comment that surfaces/documents it with a comment.
The Code Explorer (Ctrl+R), the Rubberduck toolbar, and the VBE's own Object Browser (F2) will display the contents of the VB_Description attribute, so #Description annotations are particularly useful to have on any public procedure.
Object Browser:
Code Explorer:
Rubberduck toolbar:
Add this to your class
Public Function NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
Set NewEnum = Items .[_NewEnum]
End Function
An alternative approach to this issue is not to use a Collection but a Scripting.Dictionary. One of the advantages of a scripting dictionary is that it can return arrays of the keys and items of the dictionary. Iterating over an array in VBA is a trivial exercise.
These two subs do the same thing when inside a class.
Sub DemoMe( )
Me.AboutMe ' Calls AboutMe procedure.
End Sub
Sub DemoMe( )
AboutMe ' Does the same thing.
End Sub
What is the point? Does the Me keyword do anything? What is the preferred way of an object accessing its own members?
tldr; No, although there are situations where it can be useful.
From the VBA language specification (5.3.1.5):
Each procedure that is a method has an implicit ByVal parameter called
the current object that corresponds to the target object of an
invocation of the method. The current object acts as an anonymous
local variable with procedure extent and whose declared type is the
class name of the class module containing the method declaration. For
the duration of an activation of the method the data value of the
current object variable is target object of the procedure invocation
that created that activation. The current object is accessed using the
Me keyword within the <procedure-body> of the method but cannot be
assigned to or otherwise modified.
That's all it is, just a "free" local variable that refers to the specific instance that the method is being called on. This also happens to be the default context for the procedures during their invocation, so it can be omitted if the code is intended to operate on the current instance. Although as #HansPassant points out in the comment above, it also allows the editor to bind to the interface and provide IntelliSense.
That said, there are a couple instances where you would either want to or have to use it (this is by no means an exhaustive list):
Naming collisions:
If your class has a member that "hides" a built-in VBA function, it can be used to make the scope explicit:
Public Property Get Left() As Long
'...
End Property
Public Property Get Right() As Long
'...
End Property
Public Property Get Width() As Long
Width = Me.Right - Me.Left
End Property
Equity Checks:
Public Function Equals(other As Object) As Boolean
If other Is Me Then
Equals = True
Exit Function
End If
'...
End Function
Fluent Functions:
This can be a useful pattern for compositing objects - you perform an action, then return the instance of the class so they can be "chained". Excel's Range interface does this in a lot of cases:
Public Function Add(Value As Long) As Class1
'Do whatever.
Set Add = Me
End Function
Public Sub Foo()
Dim bar As New Class1
bar.Add(1).Add(1).Add 1
End Sub
Not any more than there are reasons to use this in Java, C#, or any other language: it's a reserved identifier that represents the current instance of the class - what you do with that is up to your imagination.
What is the preferred way of an object accessing its own members?
Indeed, an object doesn't need the Me keyword to access it own public interface. Same as this in other languages, I'd even call it redundant. However it can sometimes be a good idea to explicitly qualify member calls with Me, especially when the class has a VB_PredeclaredId attribute (e.g. any UserForm): referring to UserForm1 in the code-behind of UserForm1 yields a reference to the default instance of the class, whereas qualifying member calls with Me yields a reference to the current instance of that class.
Accessing Inherited Members
VBA user code can't do class inheritance, but a lot of VBA classes do have a base class. The members of UserForm when you're in the code-behind of UserForm1, and those of Worksheet when you're in the code-behind of Sheet1, aren't necessarily easy to find. But since the inherited members show up in IntelliSense/auto-complete, you can type Me. and browse a list of members inherited from the base class, members that you would otherwise need to know about in order to invoke.
A class creating an instance of itself inside itself? That I've never seen.
You're missing out! I do this all the time, to enable referring to the object instance held by a With block, inside a Factory Method - like this GridCoord class.
Public Function Create(ByVal xPosition As Long, ByVal yPosition As Long) As IGridCoord
With New GridCoord
.X = xPosition
.Y = yPosition
Set Create = .Self
End With
End Function
Public Property Get Self() As IGridCoord
Set Self = Me
End Property
Note that while the GridCoord class exposes a getter and a setter for both X and Y properties, the IGridCoord interface only exposes the getters. As a result, code written against the IGridCoord interface is effectively working with read-only properties.
Another use is to get the name of the class module, without needing to hard-code it. This is particularly useful when raising custom errors: just use TypeName(Me) for the Source of the error.
The Builder Pattern notoriously returns Me, which enables a "fluent API" design that makes it possible to write code that incrementally builds complex objects through chained member calls, where each member returns Me (except the final Build call, which returns the type of the class being built):
Dim thing As Something
Set builder = New ThingBuilder
Set thing = builder _
.WithFoo(42) _
.WithBar("test") _
.WithSomething _
.WithSomethingElse
.Build
#PBeezy : In addition to my comment :
Me, refers to the object it's coming from so AboutMe resides in the class. If you had another instance, say this is Class1, you'd have dim c as Class1, as soon as you create an instance of Class1 in Class1, you need to tell the compiler which class you are using, the holding class or the instance created in, where, me.class1.aboutme would be logically valid. You can also create, a class for each cell in a workbook, then you could refer to A1's class from B1's class. Also, if there is a public function/sub called AboutMe, this also helps.
Class (clsPerson)
Public c1 As clsPerson
Public strPersonName As String
Public Function NAME_THIS_PERSON(strName As String)
strPersonName = strName
End Function
Public Function ADD_NEW_CHILD(strChildName As String)
Set c1 = New clsPerson
c1.strPersonName = strChildName
End Function
Normal module
Sub test()
Dim c As New clsPerson
c.NAME_THIS_PERSON "Mother"
c.ADD_NEW_CHILD "Nathan"
Debug.Print c.strPersonName
Debug.Print c.c1.strPersonName
End Sub
Gives these results
Mother
Nathan
I have an access database and I'm attempting to write some VBA to increase automation.
I have a module I've entitled Global Variables which I've successfully used to define global constants (file paths etc) and a module ReportCode which has two main subrouties, one to run a query with ADODB (scraping form params where needed - returning a recordset), and a second which takes this record set and writes the data out to an excel template.
Given I may want to have multiple queries write to multiple tabs I thought the best way was to define a ExportDocument object to contain common parameters and a OrgReport object, containing query and tab specific parameters - then gather multiple OrgReport objects in a collection.
I'd hope to then pass just these two parameters into the main subroutine. This turns out to be a pain in VBA (or at least compared to ruby!).
Here you can see how I've defined by custom objects
Option Private Module
' Define Custom Doc Object
Public Type ExportDocument
TeamName As String
TemplatePath As String
SaveName As String
SavePath As String
End Type
' Define Custom Report Object
Public Type OrgReport
Query As String
Fields As Variant
Sheet As String
StartCol As Integer
StartRow As Integer
Headers As Boolean
End Type
And here is the code in my form which then called an additional module which does the heavy lifting - I know that part works because it did before I tried to go all OOP on this...
Private Sub my_report_from_form_Click()
' Prep Query Inputs
Dim TeamX_Report As OrgReport
TeamX_Report.Query = "qry_TeamReporting Query"
TeamX_Report.Sheet = "RawData"
TeamX_Report.StartCol = 1
TeamX_Report.StartRow = 2
TeamX_Report.Headers = True
TeamX_Report.Fields = Nothing
' Prep Document Inputs
Dim Teamx_Doc As ExportDocument
Teamx_Doc.TeamName = "MyTeam"
Teamx_Doc.TemplatePath = strReportTemplatePath & "MyTeam.xltm"
Teamx_Doc.SaveName = ""
Teamx_Doc.SavePath = strReportSavePath & Teamx_Doc.TeamName
' Init and set collection for CHAIN reports
Dim TeamReports As New Collection
TeamReports .Add Item:=TeamX_Report, Key:=TeamX_Report.Query
Call export_data_dump(Teamx_Doc, TeamReports)
End Sub
This gives me the issue of:
Only public user defined types defined in public object modules can be
used as parameters or return types for public procedures of class
modules or as fields of public user defined types
Following advice here I changed
Dim Teamx_Doc As ExportDocument
to
Teamx_Doc = CreateObject("ExportDocument")
But alas now i get
Run-time error '429': ActiveX component can't create object VBA
All references to this problem seem to be related to calling code from the Word., Excel. or Outlook. codebases, so perhaps I'm just missing a prefix for my own module stored within my database?
Best lead I've found is this one, which seems to suggest there's deeper issues with what i'm trying to do, or that i may get around parts by calling Friend, though I'm lost to where and how.
Is there a way I can late bind my UDT Objects within my form code, stash one in a collection then pass both to a subroutine that will be able to grab params from the first 'Doc' object and then iterate through the second 'report' object?
VBA >_<
There's no reason I can see why this doesn't work:
Dim Teamx_Doc As ExportDocument
Especially if you're not getting an error on line
Dim TeamX_Report As OrgReport
I've used custom Public Types before - no need for CreateObject
Though the docs seem to say it's just fine, can you try removing the
Option Private Module
The error message is kinda misleading. You simply can't put variables with an User-defined Type into a collection.
Option 1: Use an array instead. This actually sounds like it would work well for what you want to do.
Option 2: Create a class module instead of an UDT for OrgReport. Then instantiate objects of that class, those you can add to a collection.
See
Excel VBA Collections and Custom Data Types
and
http://www.mrexcel.com/forum/excel-questions/16849-adding-user-defined-types-collection.html
I have been tasked with modifying some legacy ActiveX DLLs written in Visual Basic 6. One of the things I need to do is to emulate the "ScriptingContext" object, (so that we can support other mechanisms for running the DLLs other than IIS without having to re-write large chunks of the code).
Something that has been causing me some grief is the "ASPTypeLibrary.Application" object which has two very different ways to access its stored values, eg:
.Application("KeyName")
or
.Application.Value("KeyName")
How can I create my own VB6 class which supports both of these access mechanisms? I can do one or the other but not both?
(a simple code example would be great thanks, I'm not a VB6 programmer)
I have found a way to do this, see the code snippet below taken from two classes, "clsContext" and "clsContextApp". The latter implements the ".Value" functionality and the former has the ".Application" property...
I have now discovered an even more difficult problem. The ScriptingContext's "ASPTypeLibrary.Request" object has three different ways to access its ".Request.QueryString" property:
.Request.QueryString("KeyName")
or
.Request.QueryString.Value("KeyName")
or
.Request.QueryString
The last method returns a string comprised of all the Key/Value pairs concatenated by "&" characters. I have no idea how to implement this?
' clsContext
Public ContextApp As clsContextApp
Public Property Get Application(Optional ByRef Key As Variant = Nothing) As Variant
If (Key Is Nothing) Then
Set Application = ContextApp
Else
If (Not ContextApp.p_Application.Exists(Key)) Then
Application = ""
Else
Application = ContextApp.p_Application.Item(Key)
End If
End If
End Property
Public Property Let Application(ByRef Key As Variant, ByVal Value As Variant)
If (VarType(Key) = vbString) Then
If (VarType(Value) = vbString) Then
If (Not ContextApp.p_Application.Exists(Key)) Then
ContextApp.p_Application.Add Key, Value
Else
ContextApp.p_Application.Item(Key) = Value
End If
End If
End If
End Property
' clContextApp
Public p_Application As Scripting.Dictionary
Public Property Get Value(Key As String) As String
If (Not p_Application.Exists(Key)) Then
Value = ""
Else
Value = p_Application.Item(Key)
End If
End Property
Public Property Let Value(Key As String, Value As String)
If (Not p_Application.Exists(Key)) Then
p_Application.Add Key, Value
Else
p_Application.Item(Key) = Value
End If
End Property
Well I've managed to answer the additional question regarding ScriptingContext's "ASPTypeLibrary.Request" object which has three different ways to access its ".Request.QueryString" property.
I've included a code snippet below that is based on the code from my previous answer for the "ASPTypeLibrary.Application" object. If I add a new Property to the "clsContextApp" class and make it the default property for that class, then it will be called when the ".Application" property is called without any qualification eg:
MyString = Context.Application
Setting a particular property as the default property in VB6 is a little obscure, but I followed the directions I found here.
' clsContextApp Default Property
Property Get Values(Optional ByVal Index As Integer = -1) As String ' This is the Default Value for clsContextApp
Attribute Values.VB_UserMemId = 0
Dim KeyName As String, Value As String
Values = ""
If (Index < 0) Then
For Index = 0 To p_Application.Count - 1
KeyName = p_Application.Keys(Index)
Value = p_Application.Item(KeyName)
If (Index > 1) Then
Values = Values + "&"
End If
Values = Values + KeyName + "=" + Value
Next Index
Else
If (Index < p_Application.Count) Then
KeyName = p_Application.Keys(Index)
Value = p_Application.Item(KeyName)
Values = KeyName + "=" + Value
End If
End If
End Property
Adding a reference to Microsoft Active Server Pages Object Library, and to COM+ Services Type Library, and then using the object browser reveals some basic things you seem to be missing.
GetObjectContext is a global method in COMSVCSLib with no arguments used to retrieve the current ObjectContext as its return value.
ObjectContext is a Class. It has a read-only default property, named Item that takes a String argument and is of type Variant.
Passing "Application" as an argument to Item returns the current instance of the Application Class.
ScriptingContext is a Class. It is obsolete.
Application is another Class. It has a default property named Value that takes a String argument and is of type Variant.
Value is a property of the Application Class and provides access to a read-write key/value pair store where keys are always Strings. Since it is of type Variant you can store objects as well as simple values and arrays of various types.
None of this looks difficult to replicate in VB6. The key/value store could be a Collection or Scripting.Dictionary.
I have a set of owners, who each have their own set of opportunities.
I have two class modules, ClmOpportunity which has a bunch of properties, and ClmOwner which has a single name property and a Collection storing ClmOpportunity Objects:
Public name As Variant
Private opps As New collection
Public Function addOpportunity(opp As ClmOpportunity)
opp.ID = opps.Count + 1
opps.Add opp, opps.Count + 1
End Function
These owner objects are also being stored in a collection in my main module. When I try to use the function addOpportunity as shown below:
Dim item As New ClmOpportunity
item.name = "test"
owners.item(overallOwner).addOpportunity (item)
I get the error:
"object doesn't support this property or method"
I am quite new to VBA and I don't understand why this is, I am passing in a ClmOpportunity, so it should be fine right?
Any help would be greatly appreciated!
You don't use parentheses if there's no return value...
owners.item(overallOwner).addOpportunity item
...then you'll get a "type mismatch" error because a collection expects a string value as a key, so you'll need to adjust your addOpportunity function (which should probably be a Sub if you don't intend adding a returned value)