Welcome to Geekdojo Sign in | Join | Help

I really like Extension methods... especially when you combine them with other good language bits to really make a difficult problem a lot easier.  I think invoking on Windows Forms applications is one of those problems that is elegantly solved by some of these new features.  For an in-depth background on the 'whys' and details of having to use Invoke to access GUI elements from other threads in your windows forms applications, see this article

I have always found stopping to Invoke something to be a bit of a pain -  just enough effort to derail my train of thought while working on a WinForms applications.  So back in the 1.1 days, I created some helper code to try to smooth this process out a bit.  On method to help out invoke was called like this:

string value = InvokeHelper.GetProperty(myTextBox, "Text");

This was an improvement on invoking without help, but essentially all I was doing was wrapping some reflection calls to coax the value from the Text property.

Now, with extention methods, lambda expressions and the ability of the compiler to infer the resolution of generic type parameters to actual types, I was able to refactor many helper methods (variations on the above for getting and setting properties, fields, calling methods, and their overloads into two methods, this is the first:

public static TResult Invoke<T, TResult>(this T controlToInvokeOn, Func<TResult> code) where T : Control
{
   if (controlToInvokeOn.InvokeRequired)
   {
    return (TResult)controlToInvokeOn.Invoke(code);
   }
   else
   {
    return (TResult)code();
   }
}

This handles the majority of cases where I wish to invoke in WinForms, it's available on all Control instanced (though I usually just invoke on the main Form instance where the controls I want to touch are) and it works without having to specifiy the return type (or where there are no return types *except in cases where the Func code isn't an expression... see below for the second method/case).

The other method is this:

public static void Invoke(this Control controlToInvokeOn, Func code)
{
    if (controlToInvokeOn.InvokeRequired)
    {
       controlToInvokeOn.Invoke(code);
    }
    else
    {
       code();
    }
}

This method has some overlap with the previous method, but its exclusive territory is cases where you pass something to the 'code' parameter that *isn't* an expression.  In this overload, you are passing in a delegate defined as public delegate void Func() (which is not included in the framework, I just made it up though I could have easily used the ThreadStart delegate -- hmm, maybe I should have, oh well :)).  This can be a multi-step method that includes locals etc. just like any anonymous method would.  The first overload of Invoke would require you to return something at the end of your method, which I thought was ugly, so I added this second method for 'code aesthetic' purposes.

[Edit] One obvious thing I left out (you can tell I haven't blogged much recently) is is just the syntax of how this is called.  Inside a Form (this), you call the method like so (in this case, setting a value on a control on the UI thread):

this.Invoke(() => progressBar1.Value = i);

This gives you easier syntax than the out of the box invoke, and only invokes if it needs to.

Returning a value from a property is pretty easy, too:

string value = this.Invoke(() => button1.Text);

The return type is resolved implicitly so you never need to cast and normally shouldn't need to explicitly supply it.

Anyway, I would like to improve this if I can.  One question: is it wrong to overload the 'Invoke' method?  If yes, what name should these methods have?  Any other comments or questions are welcomel

Here's the code:
DemonstrateInvokeViaLambdas.zip

 

 

There just isn't much information out there about Microsoft Dynamics CRM Web Services... at least not when compared to other technologies.  And with Silverlight and Surface and PopFly burning up the MS blogosphere, it's kind of understandable.

Still, there are those of us who need to get things done in Microsoft Dynamics CRM... and so here's my little contribution. 

It's probably not obvious what I was trying to accomplish from the title of this post.  It's very simple, though.  Let's say I want to get a list of customers from the CRM filtered by criteria in a table that is several 'joins' away.  So in SQL using the sample AdventureWorks DB it looks like this:

SELECT * FROM Sales.Customer c
INNER JOIN Sales.CustomerAddress ca
   ON c.CustomerId = ca.CustomerId
INNER JOIN Person.Address a
   ON ca.AddressId = a.AddressId
WHERE City = 'Montreal'

So now my CRM Web Services goal is more clear.  I had this requirement and there wasn't much I could find about doing it, but I managed to cobble together this sample.  Keep in mind the code I'm going to show you is *not* representative of our CRM or its custom fields, it's just how you would do it, mirroring the AdventureWorks SQL snippet above.

First thing I found handy was a nice built-in entity and relationship list the CRM comes with.  Navigate to http://YourCrmServerName/sdk/list.aspx to get to the Microsoft CRM MetaData browser with all the entities you can connect to from your CRM's web service. (The service URL, btw should be http://YouCrmServername//mscrmservices/2006/crmservice.asmx).  Here's my real-but-pseudo code showing how to do this many-to-many join scenario to filter results.

CrmService cs = new CrmService();
cs.Credentials = new System.Net.NetworkCredential("user", "password", "domain");

QueryExpression customerQuery  = new QueryExpression();
customerQuery.EntityName = EntityName.customer.ToString();

// Prepare the first 'INNER JOIN' to the many to many table:
LinkEntity innerJoinOne = new LinkEntity();
// LinkFromEntityName == same as our QueryExpression.EntityName
innerJoinOne.LinkFromEntityName = EntityName.customer.ToString();
// attribute is like the 'ON' clause
innerJoinOne.LinkFromAttributeName = "customerId"; 
innerJoinOne.LinkToEntityName = EntityName.customeraddress.ToString();
// don't assume this is the same as the primary key table
// look it up in the metadata browser!
innerJoinOne.LinkToAttributeName = "customerId";

// Prepare the second join, to the table that holds the criteria
// field we want to specify
LinkEntity innerJoinTwo = new LinkEntity();
// here LinkFromEntityName should == same as the LinkToEntityName in our
// first LinkEntity
innerJoinTwo.LinkFromEntityName = EntityName.customeraddress.ToString();
innerJoinTwo.LinkFromAttributeName = "addressId";
innerJoinTwo.LinkToEntityName = EntityName.address.ToString();
linkinnerJoinTwoTwo.LinkToAttributeName = "addressId";

// now make the CRM WS equivalent of our 'WHERE' clause:
ConditionExpression where = new ConditionExpression();
where.Operator = ConditionOperator.Equal;
where.AttributeName = "city";
where.Values = new object[] { "Montreal" };

// in this case, we attach the ConditionExpression inside
// a FilterExpression, then to the LinkCriteria property
// on the 2nd LinkEntity
FilterExpression filter = new FilterExpression();
filter.Conditions = new ConditionExpression[] { condition };
innerJoinTwo.LinkCriteria = filter;

// Then you attach the 2nd LinkEntity, to the first one
innerJoinOne.LinkEntities = new LinkEntity[] { innerJoinTwo };

// Then the first one to the QueryExpression.
customerQuery.LinkEntities = new LinkEntity[] { innerJoinOne };

BusinessEntityCollection customerEntities = cs.RetrieveMultiple(customerQuery);

I hope that makes some sense, and saves some of the frustration I had in getting there.  If there's anything unclear, please comment and I'll try to answer.

Later,
Richard

I love the xml type in SQL Server 2005.  Here's a very simple way I made use of it: I audit all the object/schema changes to the database with a simple database-level trigger.

First, I create a very simple table (inside a schema I name 'Audit'):

CREATE TABLE [Audit].[Objects](

[EventID] [int] IDENTITY(1,1) NOT NULL,
[EventData] [xml] NULL,

PRIMARY KEY CLUSTERED
(
   [EventID] ASC
) WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

Then, the trigger:

CREATE TRIGGER [Trig_AuditObjects]
ON DATABASE
FOR
DDL_DATABASE_LEVEL_EVENTS
AS
INSERT
INTO Audit.Objects(EventData)
SELECT EVENTDATA()
GO
ENABLE TRIGGER [Trig_AuditObjects] ON DATABASE

That's it.. now I get a nice neat little xml entry in my table every time a DDL database level event happens, like so:

<EVENT_INSTANCE>
    <
EventType>ALTER_TABLE</EventType>
    <
PostTime>2007-06-03T20:12:05.813</PostTime>
    <
SPID>55</SPID>
    <
ServerName>CHI100906</ServerName>
    <
LoginName>MYDOMAIN\myusername</LoginName>
    <
UserName>dbo</UserName>
    <
DatabaseName>Sales</DatabaseName>
    <
SchemaName>dbo</SchemaName>
    <
ObjectName>Products</ObjectName>
    <
ObjectType>TABLE</ObjectType>
    <
TSQLCommand>
        <
SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON"      QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" />
        <
CommandText>ALTER TABLE dbo.Products
    DROP COLUMN testremove
       </CommandText>
  </
TSQLCommand>
</
EVENT_INSTANCE>

The EVENTDATA() function is provided by SQL Server inside a DDL trigger and provides all the data you see above as an xml document.

I like having this during development - it's like a poor man's source control for my schema changes.  But also, it could come in very handy for forensic purposes when diagnosing post-rollout issues or accidental schema changes.  Anyway, it's simple and handy for what it does.

A coworker of mine was trying to apply the XPath 2.0 upper-case() function to some shredded xml from a query in SQL Server 2005 and inquired about it with me just before heading out the door.

Here's what's happening: You try to apply the upper-case function (that exists in the XQuery 1.0 / XPath 2.0 standard) to, say, the result from a .value() method call on some stored xml and you get this lovely message:

Msg 2395, Level 16, State 1, Line 4

XQuery [value()]: There is no function '{http://www.w3.org/2004/07/xpath-functions}:upper-case()'

And turns out it's not lying....  per Michael Rys in this post on microsoft.public.sqlserver.xml the standard is not fully implemented (even though it refers to it in the error message, and browsing http://www.w3.org/2004/07/xpath-functions does show upper-case() as a function).   But SQL Server only supports:

concat
contains
substring
and
string-length

on strings :(

Well, I finally brought my first career (recording engineer) together with my love of the pop-culture phenom of mashups to release my first mashup to the world:

Unbelievable Disco Lolita Tale to Tale

That's Alizée's international hit "Moi Lolita" with overdubs of Love and Rocket's "No New Tale to Tell", U2's "Discothèque" and EMF's "Unbelievable".

It was fun to do, but I'm not sure I'll have much time to do more - in the meantime, hope you like it.

 

Ryan introduced me to the MS idea of "Pit of Success" where you just fall into using things the right way from the obvious way their API indicates use (form shows function).  I'm simplifying, I'm sure, but that's the gist.

In trying to get the Sql Server 2005 thesaurus to work, I have stumbled into the opposite: a pit of failure.  We have all felt ourselves fall into this pit at one time or another with a product or framework or API we've used.  One where we curse under our breath and promise ourselves that we will never force consumers of our APIs to go through what we are now.

Such is the thesaurus feature of Sql Server 2005's full-text search functionality.  Among the attributes of this particular feature that single it out for 'constructive criticism':

  • The naming scheme of the files is based in 3-digit language codes.  This means the tsEng.xml is NOT the thesaurus you edit, Americans (that's me, btw) it's tsEnU.xml - okay, I'm just an idiot on this one.  But it's still, imo, a pit of success violation.
  • The xml files are written in unicode with byte order marks and they cease to work if the byte order marks aren't written with the text file is. TextPad, which, oh, a few developers use here and there ;) doesn't write the byte order marks by default.  Maybe that's TextPad's fault, but frankly, IE can render lots of web pages with some pretty effed-up html and make them human readable, why can't they load a stupid xml file for an English-localized db without byte marks?
  • The thesaurus files are read into memory after a Sql Server service restart (well, technically at the first catalog query after a restart) and can't be changed without the server being restarted!
  • Certain benign-looking data arrangements actually cause the entire thesaurus to fail to work.  I'm not certain of all of them, but it appears that having an <expansion> tag where one of the substitution words is a contained in another word is one case.  So if you have <expansion><sub>cover</sub><sub>recover</sub></expansion> it appears to me your entire thesaurus will fail to work.  Combine that with having to re-start sql sever to test the fixes to your problems and you get a lot of wasted tiem.
  • When something in the file fails, causing the whole thing to fail, it writes an unhelpful entry to the windows application log (not the sql server logs).
  • The thesaurus file is per instance (AFAICT) meaning if you want a diff. thesaurus for a different application's full-text catalog, you have to install another (named) instance on your server - double ug.

I don't know about you, but, frankly, I could have written a nice tokenizer and synonym injection API in the time I took trying to figure out the crappyness of the thesaurus xml.

It's too bad, because in Sql Server 2005 there are some pretty stunning pits of success.  The SMO, for me, was like this.  Objects to manage your database that made a lot of sense.   Anyway, I'm done with the thesaurus.  I can whole-heartedly NOT recommend it. 

 

[Edited to correct my stupidity about the Unicode thing]

<sigh>  After a few frustrating hours trying to get the full-text indexing search in Sql Server 2005 to use the darn thesaurus file I was writing, I happened upon this newsgroup post in which the guy was having the exact same problem as me.  In essence, Sql Server expects a unicode file with byte order marks written to it, which TextPad doesn't do by default.  Changing TextPad to save as unicode with byte order marks solves the problem.

I'm hoping the lots of superfluous and gratuitous keywords added to this post (like "Sql Server 2005", and "full-text search" and "thesaurus" "not working") will mean that someone else out there is saved the trouble I had.

 

 

This was enough of a PITA that I'm going to blog it in hopes of keeping someone else from feeling my pain.

The goal: in Sql Server 2005, add a linked server that is the local indexing service [edit to add: I don't think it's possible to connect to a remote server] and then query using the OPENQUERY function.  It's actually pretty simple, but I was getting frustrated, so here we go.

In Management Studio, expand Server Objects-->Linked Servers.  Right click and select "new linked server".  It might also be handy to bring up the Indexing Service's snap-in control panel to reference some info.  It's on the Computer Management console under Administrative Tools, or just go Start-->Run--> ciadv.msc

  • In the dialog that comes up, fill in "Linked Server" textbox with any arbitrary name you want. 
  • Make sure the "Other data source" radio button is checked. 
  • Select "Microsoft OLE DB Provider for Indexing Service" (I just tab to the drop-down and hit the END key 'cause it's the last entry on the provider list).
  • Fill in the "Data Source" field with the name of the catalog from your index server.  Don't know the name? Look at the list in the indexer server control panel you opened. 

OK, and you're done adding the linked server.  Querying from the index is not what I was used to and finding information required a lot of Google-Fu.  Here's a simple query from a project of mine where the "Linked Server" name is 'KnowledgeDocs'.

SELECT * FROM OpenQuery(KnowledgeDocs, 'Select Directory, FileName, Size FROM SCOPE()')

You'll notice a thing called Scope() is being used as the 'table' here.  Scope provides functionality as a table, but also for further isolating which directory in your catalog you want to hit with the query.  Here's a slightly more complex query illustrating that:

SELECT * FROM OpenQuery(KnowledgeDocs, 'Select Attrib,Directory, FileName, Size FROM SCOPE(''DEEP TRAVERSAL OF "C:\Somewhere\1.1\Documents"'')')

You can see I've added some stuff inside the SCOPE function.  Well, just so it's clear, let me spell out what's in there.  First, because we're passing this query through as text, and since the SCOPE function wants a single quote, we're first escaping our single quote by doubling it.  Then come the string-within-a-string representing the clause/parameters telling the scope function to go through every sub-folder of a particular search location.

There are a lot of differences in the SQL language implementation for Indexing Service.  Here's the MSDN link (was hard for me to find!) on the query syntax:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/indexsrv/html/ixrefqls_3wj7.asp?frame=true

Here's an example of a slightly more complicated query:

SELECT * FROM OpenQuery(KnowledgeDocs, '
SET RANKMETHOD DICE COEFFICIENT
SELECT Rank, Directory, FileName, Size
FROM SCOPE(
''DEEP TRAVERSAL OF "C:\Somewhere\1.1\Documents"'',
''SHALLOW TRAVERSAL OF "C:\SomewhereElse\Documents"'')
WHERE CONTAINS(''CME'')
AND CONTAINS('' "gateway" NEAR() "network" '')
AND CONTAINS('' FORMSOF(INFLECTIONAL, "connect") '')AND FREETEXT(''install network gateway'')
AND ( FileName LIKE ''%.htm'' OR FileName LIKE ''%.doc'')
ORDER BY RANK DESC'
)

I hope his has been helpful...

Richard

Just want to save this article on FTP stuff for .NET 2.0.  Had a need in the past and may again!

http://www.codeproject.com/vb/net/FtpClient.asp

Just want to save this mapping URL on MSDN of SqlDbType to SqlType (the 'native' sql types you should use in Sql CLR code), since I expect to be doing some of that, shortly!

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataSqlTypes.asp

[ETA: duh, you actually have to add the link]

Just linking to an interesting discussion (vis a vis the comments) of closures and C# 2.0:

http://blogs.msdn.com/abhinaba/archive/2005/10/18/482180.aspx

I thought this was a good way to get some of your life back from spam!

Recently spotted...

I would not post such a thing as this if I had not spent a LOT of time looking at it.  But I have, and the new Application Updater Block v2.0 has been neither easy to implement nor reliable in even toy problems for me.  I'm extremely disappointed.  I understand that I probably don't understand properly how to use it.  But I also know I'm from from an idiot and walking through the step-by-step instructions and QuickStarts multiple times should have made it relatively easy to implement an out-of-the box auto-update.  Not the case.

I've googled. I've followed the help instructions. I've modeled the QuickStarts.  I've Uninstalled and reinstalled.  Recompiled the AppBlocks and Ent. Library using some of the various different build configs (why does Release compile but ReleaseFinal does not??).  Regardless of these efforts I have had to step into code everywhere just to figure out that my trivial updates weren't downloaded by the BitsDownloader or why my error events weren't firing when Bits encountered a COM exception etc. etc.

I know a lot of brilliant people worked on this thing, and I know it's highly configurable and extensible (or is supposed to be).  But like with most generic purpose, complex things, it's NOT easy to use and does require specialized knowledge of its inner workings in even the most basic case of true end-to-end auto updates.  If anyone wants to step up and point me to (or write!) a no-fail guide to a simple vanilla implementation of AUB2.0 with a Windows app I'll be grateful.  But I won't believe it's simple or easy when I have, with great humilty and focus tried to implement auto update with it several times and faced non-trivial roadblocks at each one.

VS.NET is a funny thing, it won't let you reference an .exe and yet csc.exe will.  That's a bit of a pain when you want to reference, say, a WinForm for unit testing purposes.  Sure, you can create a separate assembly project for the entry point leaving your forms project as a class library, but I have enough projects crowded in my solution as it is - plus it just feels wrong to me.

So I put this pre and post build event in my WinForms project and set my unit test project to be dependant on my WinForms projects build and voila! I get a reference assembly that VS.NET can live with via a hard link.

Pre build event:
erase /q $(TargetFileName).dll

Post build event:
fsutil hardlink create $(TargetFileName).dll $(TargetFileName)

Then I just reference the resulting .dll as a normal reference and everything just works.

More Posts Next page »