ms-access localization and default boolean values - ms-access

Our access client generates on the fly SQL inserts, update and delete instructions to be sent on a MS-SQL Server. Most users have the runtime version of Access 2007, and a few use the complete MS-Access version, 2003 or 2007. This morning one of our new users abroad, using a french/complete version of Access 2003, was unable to update data containing boolean fields.
It appeared that these fields are, in the french version of Access, populated with "Vrai/Faux" instead of "True/False" values. The problem was solved by installing the 2007 access runtime.
But I'd like to find a permanent solution, where I'd be able to read from somewhere which localized version of Access is in use and 'translate' the localized True/False values to standard True/False. I already checked the regional settings of the computer without success, so it is somewhere else. Any idea?
EDIT: Following JohnFX proposal, it is effectively possible to convert from local True/False to universal True/False with this simple function:
Function xBoolean(xLocalBooleanValue) as Boolean
if cint(xLocalBooleanValue) = -1 Then
xBoolean = True
endif
if cint(xLocalBooleanValue) = 0 Then
xBoolean = False
endif
end function
EDIT: following #David's comments, I changed the favorite solution. His proposal is smarter than mine.
EDIT: I am getting the Vrai/Faux values by reading the value of a field in a recordset:
? debug.print screen.activeForm.recordset.fields(myBooleanField).value
Vrai

True is NOT FALSE, or NOT 0, in all cases, no matter the localization or the database format.
So, if you replace all tests for True with NOT 0 and all tests for False with =0, then you've avoided the issue of localization of the Access keywords (I'm surprised that VBA and the Jet and Access expression services would not still understand True/False, though), as well as whichever convention your database engine uses for storing Boolean values.
In general, your data access layer ought to be abstracting that away for you. Both ODBC and ADO do it automatically, so you work with the Boolean values you know and it's taken care of for you transparently, in my experience.
I'm also still puzzled about the question, as it sounds like a display/formatting issue, but use NOT 0 and =0 for True and False avoids the problem entirely in all cases.
EDIT: In regard to the function edited into Philippe's question:
Is there a reason you've implicitly defined your function's parameter as a variant? Is that what you mean? If it's passed a Null, it's going error out on the first CInt(), as CInt() can't accept a Null.
Also, there's a logic problem in that in VBA any number but 0 is supposed to return True. It's also completely redundant code. This is simpler and returns the correct result in all cases:
Function xBoolean(xLocalBooleanValue As Vriant) as Boolean
If CInt(xLocalBooleanValue) <> 0 Then
xBoolean = True
End If
End Function
Or, pithier still:
Function xBoolean(xLocalBooleanValue As Variant) as Boolean
xBoolean = (CInt(xLocalBooleanValue) <> 0)
End Function
And to handle Nulls passed in the parameter:
Function xBoolean(xLocalBooleanValue As Variant) as Boolean
xBoolean = (CInt(Nz(xLocalBooleanValue, 0)) <> 0)
End Function
I'm not sure that's necessary in the context you're currently using it, but I always hate writing code where I can imagine a case where it will error out -- even if I know it can't break in its present context, you never know where it might end up getting used, so should you anticipate a condition that can be handled, you should handle it.
Premature optimization?
No -- it's putting a safety lock on a weapon that keeps it from being misused.
(on the other hand, if it took more lines of code to handle the antipated error than the function started out with, I'd think twice about it)

Have you considered using -1/0 (Access is weird about booleans) instead of true/false in your update and delete queries?
Math is the universal language, yaknow.
Also, to avoid having to localize the UI so much, why not use check-boxes instead of a text field for booleans on your UI?

Simple:
Function xBoolean(bool As Variant) As Boolean
xBoolean = Abs(Nz(bool, 0))
End Function

Related

Me syntax working instead of writing full control adress. Why?

I am kind of new to VBA programming in Access and I have banged upon strange thing. I have system of myListbox (multi-choice, two of them in testing regime, planning 5 total) to filter myReport contained in subform container by selected items in those listboxes.
Switching "On" filter works kind of fine (I will address it in different question after doing some research first), but switching filter off turned out to be problem. I was getting error message described here.
Managed to find a workaround. Since I have myReport bound to control in parent form, I am not switch off filter, instead I am changing it to resemble the bound field criteria, after that switch it off.
Code:
Forms![myForm]![myReport].Report.Filter = "((sourceQuery.fieldForBoundControl)=Forms![myForm]![boundControl])"
Me.FilterOn = False
but if I try this:
Forms![myForm]![myReport].Report.Filter = "((sourceQuery.fieldForBoundControl)=Forms![myForm]![boundControl])"
Forms![myForm]![myReport].Report.FilterOn = False
...or any other combination, or simply turning it off, I get error messages and procedure termination. Why?
Is my solution correct for future working of the app or I my setting myself for another Access trap?

Syntax Error: Comma in Query Expression of SQL Statement Access?

I've been trying to make this statement work for some time but have ultimately lost the will to live.
In essence, I am attempting to filter a report to only certain Locations (So if you want the query to only include locations "Avonmouth" and "Bedford", that's what it would include, filtering the rest out) I have done this by implementing a check box system, so you can easily add locations to the filter. Unfortunately I keep getting syntax problems with the SQL script. Its a bit of a butchery, so please forgive me, but I have included the SQL below. (CHKBE = The check box)
WHERE QryTraining IN ((IIf [Forms]![ReportDeployer]![CHKAV]<>"" ,"Avonmouth",x),(IIf [Forms]![ReportDeployer]![CHKBA]<>"" ,"Basingstoke",x),(IIf [Forms]![ReportDeployer]![CHKBT]<>"" ,"Bedford Transport",x),(IIf [Forms]![ReportDeployer]![CHKBW]<>"" ,"Bedford Warehouse",x),(IIf [Forms]![ReportDeployer]![CHKBE]<>"" ,"Belfast",x),(IIf [Forms]![ReportDeployer]![CHKCA]<>"" ,"Carluke",x),(IIf [Forms]![ReportDeployer]![CHKEX]<>"" ,"Exeter",x),(IIf [Forms]![ReportDeployer]![CHKKI]<>"" ,"Kidderminister",x),(IIf [Forms]![ReportDeployer]![CHKKN]<>"" ,
"Knowsley",x),(IIf [Forms]![ReportDeployer]![CHKTE]<>"" ,"Teva",x),(IIf [Forms]![ReportDeployer]![CHKWI]<>"" ,"Wickford",x),(IIf [Forms]![ReportDeployer]![CHKYO]<>"" ,"York",x))
Each time I attempt to run it, it throws it back with a Syntax error.
Thanks in advance, T.
Not sure this will work any better but parens are in wrong place at beginning of each IIf() and maybe need apostrophe delimiters:
WHERE QryTraining IN (IIf([Forms]![ReportDeployer]![CHKAV]<>"" ,"'Avonmouth'",x), IIf([Forms]![ReportDeployer]![CHKBA]<>"","'Basingstoke'",x), IIf([Forms]![ReportDeployer]![CHKBT]<>"","'Bedford Transport'",x), IIf([Forms]![ReportDeployer]![CHKBW]<>"","'Bedford Warehouse'",x), IIf([Forms]![ReportDeployer]![CHKBE]<>"","'Belfast'",x), IIf([Forms]![ReportDeployer]![CHKCA]<>"","'Carluke'",x), IIf([Forms]![ReportDeployer]![CHKEX]<>"","'Exeter'",x), IIf([Forms]![ReportDeployer]![CHKKI]<>"","'Kidderminister'",x), IIf([Forms]![ReportDeployer]![CHKKN]<>"","'Knowsley'",x), IIf([Forms]![ReportDeployer]![CHKTE]<>"","'Teva'",x), IIf([Forms]![ReportDeployer]![CHKWI]<>"","'Wickford'",x), IIf([Forms]![ReportDeployer]![CHKYO]<>"","'York'",x))
Probably need to use empty string or some text like "N/A" in place of the x.
A BOUND checkbox must be either True or False (Yes/No), never an empty string. An UNBOUND checkbox can be set for triple state, in which case it could return True/False/Null, again never an empty string. So I am not sure why comparison to empty string. Just test for True.
IIf([Forms]![ReportDeployer]![CHKAV], "'Avonmouth'", "")

Is this really a possible InvalidOperationException?

I have a Linq2Sql query that looks like this:
var data = from d in dc.GAMEs
where (d.GAMEDATE + d.GAMETIME.Value.TimeOfDay) >= DateTime.Now
&& d.GAMESTAT == 'O' && d.GAMETYPE == 0 select d;
Resharper is underlining the "d.GAMETIME.Value.TimeOfDay" in blue and telling me it's a possible System.InvalidOperationException. While I get that if it were C# code, referencing Value without checking if it has a value would be such, i'm not sure if that is true of a Linq query.
The actual SQL generated looks horrendous, and makes me want to burn my eyes out, but I see nothing that looks like it could be a null reference. Can I safely ignore this?
(ignore for the moment the other issues, such as if it returns the expected results)
EDIT:
Upon further thought, I can see how the above might cause an exception in a LinqToObjects query, and possibly other kinds (XML?). So yeah, I suppose Resharper is just being safe.
When dealing with expression trees (as this LINQ to SQL query) it totally depends on the LINQ provider used (in your case LINQ to SQL). Therefore, it is (almost) impossible for Resharper to say anything useful about your query. I think it just interprets this code as normal C# delegates. I would say it is safe to ignore, but perhaps add a comment for the next developer.
Without seeing the data schema my guess is that GAMETIME is Nullable<DateTime> - i.e. maps to datetime/time field in the DB that can be null. Resharper is simply giving you a static analysis warning that you are referencing Nullable<T>.Value without checking that it has a value.
You can rewrite the query in this way:
var data = from d in dc.GAMEs
where (d.GAMEDATE + (d.GAMETIME.HasValue ? d.GAMETIME.TimeOfDay : new TimeSpan())) >= DateTime.Now
&& d.GAMESTAT == 'O' && d.GAMETYPE == 0 select d;
The above query will just use a TimeSpan of 0 when GAMETIME is NULL.
Considering that GAMEDATE is a non-nullable database field and GAMETIME is a nullable one, I recommend that you make GAMETIME non-nullable too. This way the two fields are consistent and do not need extra logic to handle NULL values.
EDIT I have just confirmed that trying to call Nullable<T>.Value does indeed throw InvalidOperationException, not NullReferenceException.
From the horse's mouth (bolding is mine):
The two fundamental members of the
Nullable structure are the HasValue
and Value properties. If the HasValue
property for a Nullable object is
true, the value of the object can be
accessed with the Value property. If
the HasValue property is false, the
value of the object is undefined and
an attempt to access the Value
property throws an
InvalidOperationException.

Why am I getting "Invalid Cast" when using Linq to SQL?

I am a bit of a newbie when it comes to Linq to SQL but I hope you can help out. I've written the following Linq to SQL statement with Extension Methods:
Cedb.ClassEvents.Where(c => c.ClassID == 1).Select(c => c).Single()
Where Cedb is the Datacontext, ClassEvents is a table (for classes and events being held at a facility) and ClassID is a unique integer key.
This query runs fine in LinqPad (without Cedb). When it returns, it says that the return type is "ClassEvent". In Intellisense in Visual studio, it tells me that the return type of this query is ClassEvent (created in my data model). However, when I try to place the results in a variable:
var classEvent = Cedc.ClassEvents.Where(c.ClassID == 1).Select(c => c).Single();
then I get an error: InvalidCastException: Specified cast is not valid. The same thing happens if I use the "ClassEvent" class in place of the var. I'm new to this but this one seems like a true slam dunk rather than a bug. Is there something about the Single method that I don't know that is leading to the error? Any help would be appreciated!
Slace - and any other interested parties. The cause of the "Invalid Cast Exception" error was a change in the underlying data model. A smallint field had been changed to bit. Thus, when the system tried to map the query results onto the "ClassEvent" data structure, the conflict between the model (which had not been updated) and the data table emerged.
Nonetheless, I do appreciate the answer!
You don't need to do both a Select and a Single, in fact, you don't even need the Where, you can get away with (see http://msdn.microsoft.com/en-us/library/bb535118.aspx):
var classEvent = Cedc.ClassEvents.Single(c => c.ClassID == 1);
I'd also recommend against using Single unless you're 100% sure that the Func<T, bool> will always return a value, as if it doesn't return a value you will have an exception thrown. Better is using SingleOrDefault and doing a null check before interaction with the object (http://msdn.microsoft.com/en-us/library/bb549274.aspx)

Custom code in Reporting Services report

In Reporting Services I would like to add a parameter that contains data from a custom code block. Ideally, I would be able to run the following code (this is a simple testing example):
Function GetPeriods() As String()
Dim values As System.Collections.ArrayList =
New System.Collections.ArrayList()
For i as integer = 1 to 24
values.Add(i)
Next
Return values.ToArray()
End Function
and put the following in the "Text Field" of the parameter:
=Code.GetPeriods()
However, when I run the report, the parameter I apply this to is disabled and empty. Is there a different technique that should be used? Or am I doing something wrong?
If you're using SQL 2008 Reporting Services then you can have a look at this page which introduces the concept of using custom assemblies.
If you're using SQL 2005 Reporting Services then this link is the one you want.
It's a mostly trivial thing, simply compile your code into a class library and follow the instructions provided to allow your report to reference it.
You are returning an array item (an array of strings) into a text field. Instead, try returning a plain string. That should work. If you would still like to return an array list, you must basically bind it to a list control in your RDL. You can definitely do that with dataset extensions. However, I am not sure if there is any other easy way. Check the proprties of the list control and see if it allows you to directly bind to an array list.
You can create the same stored procedure on SQL Server and load parameter values from that procedure.
To access your members/functions implemented in custom code of SSRS report you should set the access modifier to "Public":
Public Function GetPeriods() As String
...
see article Writing Custom Code in SQL Server Reporting Services
I've been trying to do this same thing, set a simple list of parameter values from report code. None of the links in any of these answers shows how to do this and after quite a bit of digging around I don't think it's even possible. Yes it is possible to get the values from a database query, from a web service, or from a custom assembly, but each of these creates a lot of overhead compared to getting the list from a simple function call like =Code.GetValues(), where the function uses a For loop to create the values.
msvcyc is correct in pointing out that the parameter is expecting a string value, but the function is returning an array. I changed the return type to Array as suggested by prashant sable, but the select list is still grayed out, it does not work. And coldice is correct in saying that the access modifier should be Public.
In my digging around I found an article by James Kovac from 2005 that pointed out why this is not possible. The Parameters class has a get method, but no set method. In the VS 2008 object browser for SSRS 2008 the object name has changed, but it still does not contain a set method (see Microsoft.ReportingServices.Interfaces.IParameter.Name or .Value).
My current workaround is to just hard code the list of values, but if your value list needs to be dynamic then your only choices are database queries, web services, or custom assemblies. I think the easiest workaround of these three is to get the values from the database engine, as suggested by oleksiy.t, as long as you can write a query to return the value list you want. Your list of integers, or my list of time intervals, would both be easy queries to write. Otherwise you will need to use one of the other two workarounds.
I checked your code. The only thing that's wrong is your function returns String(). When I changed your method signature to return Array, it worked fine, in my report.
Change the signature to Function GetPeriods() As Array
Everything I've seen requires parameters and their respective settings to be part of the RDL.
That being said, if you're going to "hardcode" the values, you could create a dataset just for the report, perhaps in XML, or if it needs to be programmatically driven, do it in a web service.