Thursday, February 19, 2015
C# Delegates, Actions, Funcs, Lambdas–Keeping it super simple
C# Delegates, Actions, Funcs, Lambdas–Keeping it super simple
RATE THIS
2 Mar 2012 4:46 PM
Syntactic Sugar? Maybe.
[ Updated to reflect a reader’s observation – missing Func<> coverage. Now included ]
I was speaking with a colleague of mine today, Bret Stateham (http://bretstateham.com) and I was explaining my ignorance to some well established C# language constructs. I’ve always avoided language constructs that translate into “less typing.” What I mean is, delegates, actions, and lambdas can be totally avoided and you can still build the most sophisticated software. The compiler steps in at compile time and generates the IL for you (intermediate language), that language that gets translated into CPU-specific machine language at runtime by the CLR (Common language runtime).
Conceptually, and for the most part, it looks like Figure 1 below. The “Regular C# code” is probably skipped, meaning the fancy C# code ends up directly as IL. Anders Hejlsberg, the creator of C# (and the former creator of Borland’s Turbo Pascal) has spoken in detail about the inter-workings of the C# compiler in the past.
Figure 1
[ BTW, one day I’ll talk about what an amazing human being Anders is. He is the most humble, approachable, computer science genius I’ve ever met ].
Anders Hejlsberg Interview | http://www.microsoft.com/downloads/details.aspx?FamilyID=B202A125-DC9C-495A-8A5A-7BF98BECACE2&displaylang=e&displaylang=en |
Let’s start with a simple delegate example
A delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++. Unlike C function pointers, delegates are object-oriented, type safe, and secure. The type of a delegate is defined by the name of the delegate.
Code is the best teacher.
Part 1 | Helps the compiler with type safety |
Part 2 | One of the methods associated with the delegate |
Part 3 | The other method associated with the delegate |
Part 4 | Our object used to demo delegates with |
Part 5 | Declare a delegate and attach a method from the demo object |
Part 6 | Declare a delegate and attach the other method from the demo object |
Part 7 | Exercise the first delegate. In other words, use it to do work. |
Part 8 | Exercise the second delegate. In other words, use it to do work. |
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ModernLanguageConstructs { class Program { // Part 1 - Explicit declaration of a delegate (helps a compiler ensure type safety) public delegate double delegateConvertTemperature(double sourceTemp); // A sample class to play with class TemperatureConverterImp { // Part 2 - Will be attached to a delegate later in the code public double ConvertToFahrenheit(double celsius) { return (celsius * 9.0/5.0) + 32.0; } // Part 3 - Will be attached to a delegate later in the code public double ConvertToCelsius(double fahrenheit) { return (fahrenheit - 32.0) * 5.0 / 9.0; } } static void Main(string[] args) { // Part 4 - Instantiate the main object TemperatureConverterImp obj = new TemperatureConverterImp(); // Part 5 - Intantiate delegate #1 delegateConvertTemperature delConvertToFahrenheit = new delegateConvertTemperature(obj.ConvertToFahrenheit); // Part 6 - Intantiate delegate #2 delegateConvertTemperature delConvertToCelsius = new delegateConvertTemperature(obj.ConvertToCelsius); // Use delegates to accomplish work // Part 7 - delegate #1 double celsius = 0.0; double fahrenheit = delConvertToFahrenheit(celsius); string msg1 = string.Format("Celsius = {0}, Fahrenheit = {1}", celsius, fahrenheit); Console.WriteLine(msg1); // Part 8 - delegate #2 fahrenheit = 212.0; celsius = delConvertToCelsius(fahrenheit); string msg2 = string.Format("Celsius = {0}, Fahrenheit = {1}", celsius, fahrenheit); Console.WriteLine(msg2); } } } |
C# Actions – More sugar, please
You can use the Action(Of T) delegate to pass a method as a parameter without explicitly declaring a custom delegate. The sugar here is you don’t have to declare a delegate. The compiler is smart enough to figure out the proper types.
But you pay a price in terms of a limitation. The corresponding method action must not return a value. (In C#, the method must return void.)
Part 1 | The Action syntax avoids the use of a declared delegate. Everything is inline. |
Part 2 | The Action syntax avoids the use of a declared delegate. Everything is inline. |
Part 3 | Execute the corresponding Action code |
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ModernLanguageConstructs { class Program { static void Main(string[] args) { // Part 1 - First action that takes an int and converts it to hex Action<int> displayHex = delegate(int intValue) { Console.WriteLine(intValue.ToString("X")); }; // Part 2 - Second action that takes a hex string and // converts it to an int Action<string> displayInteger = delegate(string hexValue) { Console.WriteLine(int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber)); }; // Part 3 - exercise Action methods displayHex(16); displayInteger("10"); } } } |
Func<> Delegates
This differs from Action<> in the sense that it supports parameters AND return values.
You can use this delegate to represent a method that can be passed as a parameter without explicitly declaring a custom delegate. The encapsulated method must correspond to the method signature that is defined by this delegate.
This means that the encapsulated method must have one parameter that is passed to it by value, and that it must return a value.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ModernLanguageConstructs { class Program { static void Main(string[] args) { // Part 1 - First Func<> that takes an int and returns a string Func<int, string> displayHex = delegate(int intValue) { return (intValue.ToString("X")); }; // Part 2 - Second Func<> that takes a hex string and // returns an int Func<string, int> displayInteger = delegate(string hexValue) { return (int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber)); }; // Part 3 - exercise Func<> delegates Console.WriteLine(displayHex(16)); Console.WriteLine(displayInteger("10")); } } } |
Lambdas – Syntactic Sugar Squared
I’ve been staring at Lambdas for years and for whatever reason they don’t come natural to me. Maybe I need to spend more time in a functional language like F# to make them a natural construct.
A lambda expression is an anonymous function that can contain expressions and statements, and can be used to create delegates or expression tree types.
All lambda expressions use the lambda operator =>, which is read as "goes to". The left side of the lambda operator specifies the input parameters (if any) and the right side holds the expression or statement block.
The lambda expression x => x * x is read "x goes to x times x."
Part 1 | Declare 2 lambda expressions |
Part 2 | Run them. |
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ModernLanguageConstructs { class Program { static void Main(string[] args) { // Part 1 - An action and a lambda Action<int> displayHex = intValue => { Console.WriteLine(intValue.ToString("X")); }; Action<string> displayInteger = hexValue => { Console.WriteLine(int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber)); }; // Part 2 - Use the lambda expressions displayHex(16); displayInteger("10"); } } } |
Lambdas and Queries
Lambda expressions can also be used to simplify queries.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ModernLanguageConstructs { class Program { static void Main(string[] args) { // Part 1 - ordinary list object List<string> listPets = new List<string>(); // Part 2 - Queryable list object IQueryable<string> queryPets = listPets.AsQueryable(); listPets.Add("dog"); listPets.Add("cat"); listPets.Add("iguana"); // Part 3 - Lambda Expression (does not use curly braces) string result1 = listPets.First(x => x.StartsWith("d")); Console.WriteLine(result1); // Prints "dog" // Part 4 - Lambda expressions using iQueryable interface string result2 = queryPets.First(x => x.StartsWith("ig")); Console.WriteLine(result2); // Prints "iguana" // Part 5 - Lambda Statement (uses curly braces) // Supports the return statement result1 = listPets.First(x => { return x.EndsWith("a"); }); Console.WriteLine(result1); // Prints "iguana" // Part 6 - Does not compile // A lambda expression with a statement body // cannot be converted to an expression tree // result2 = queryPets.First(x => { return x.EndsWith("e"); }); // Part 7 - Does compile using the Func<T> syntax // You can pass in a lambda expression and it // will be compiled to an Expression(Of TDelegate). string result3 = queryPets.First((Func<string, bool>) (x => { return x.EndsWith("g"); })); Console.WriteLine(result3); // Prints "dog" // Part 8 - Convert to IQueryable IEnumerable<string> result4 = listPets.AsQueryable().Where(pet => pet.Length == 3); foreach (string pet in result4) Console.WriteLine(pet); // Prints "dog" then "cat" } } } |
Want to help?
If you’ve got some super simple examples to demonstrate advance language features, please forward them tobterkaly@microsoft.com.
The next dragon I want to slay is Dependency Injection and Inversion of Control. I want to explain these two concepts in as little code as possible. Hope you got some value out of this post.
Thursday, February 12, 2015
XrmSvcToolkit.Fetch Example
This function demonstrate the use of Fetch to return the BusinessUnitName for the current user.
function GetBusinessUnitName() {
var lGetBusinessUnitName;
var fetchXml = "<fetch mapping='logical'>" +
"<entity name='systemuser'>" +
"<attribute name='businessunitid' />" +
"<filter type='and'>" +
"<condition attribute='systemuserid' operator='eq-userid' />" +
"</filter>" +
"</entity>" +
"</fetch>";
XrmSvcToolkit.fetch({
fetchXml: fetchXml,
async: false,
successCallback: function (result) {
lGetBusinessUnitName = result.entities[0].businessunitid.Name;
},
errorCallback: function (error) {
throw error;
}
});
return lGetBusinessUnitName;
}
function GetBusinessUnitName() {
var lGetBusinessUnitName;
var fetchXml = "<fetch mapping='logical'>" +
"<entity name='systemuser'>" +
"<attribute name='businessunitid' />" +
"<filter type='and'>" +
"<condition attribute='systemuserid' operator='eq-userid' />" +
"</filter>" +
"</entity>" +
"</fetch>";
XrmSvcToolkit.fetch({
fetchXml: fetchXml,
async: false,
successCallback: function (result) {
lGetBusinessUnitName = result.entities[0].businessunitid.Name;
},
errorCallback: function (error) {
throw error;
}
});
return lGetBusinessUnitName;
}
Copy Invoice functionality: Example of ribbon modification + XrmSvcToolkit advanced Javascripting
Copy Invoice functionality: Example of ribbon modification + XrmSvcToolkit advanced Javascripting
For our internal CRM we needed a functionality that enables you to copy an invoice with all of the related invoice products. This is a very useful functionality for monthly maintenance invoices. So I decided to create a ribbon button and use XrmSvcToolkit to manipulate with the data.
This will be a more technical blog post and I’m assuming that you already know the basics of ribbon customization and JavaScript. If you’re not familiar with ribbon customization there is a lot of available material on the web, and if you want to learn about XrmSvcToolkit you can read my previous blog post.
We’ll divide this post in two parts:
- Creating the ribbon button
- Implementing the business logic
1. Creating the ribbon button
So first off we need an icon for the ribbon button. Find a suitable 32×32 png transparent icon and upload it to CRM. I gave it a schema name new_copyinvoice.png.
Next we need a JavaScript library that will hold our function that will do the copying. So create a new one and give it a schema name new_copyinvoice. Create and empty function that accepts argument invoiceid.
function copyInvoice(invoiceid) { }
Now we’re ready to create our ribbon button. Since manual ribbon customization is quite hard and error prone I’m using the Visual Ribbon Editor. So fire it up, connect to your CRM and open the Invoice ribbon. Choose the ribbon type Homepage and create a new button after the Edit button. You can see the settings for the button in the following pictures. In the enable rules we configure that the button is only enabled if a single invoice is selected.
Now we need to specify what happens when you click the button. So in the action tab we add a new JavaScript function action and call our function from the library. We then need to pass a Crm parameter to the function so add a new CrmParameter of type FirstSelectedId. This will pass the guid of the selected invoice to the function.
Click save the ribbon and now you should see your button displayed.
Now here comes the tricky part. Since we will be using the XrmSvcToolkit to read and create data we need to load it up when the ribbon loads. How do you achieve that? Well a small trick is to export the RibbonEditor solution, unzip it and edit the customization.xmlfile. Add two Javascript actions that call isNan function (is not a number function). We call this function just to get the library loaded. Zip the solution and load it up in CRM.
Now we’re ready for the second part.
2. Implementing the business logic
Since we receive in our function only the invoice guid, first we need to fetch the invoice entity. We also need to get the server url since we’’ll need it to open the newly created invoice.
var invoice; var serverUrl = Xrm.Page.context.getServerUrl(); XrmSvcToolkit.retrieve({ entityName: "Invoice", id: invoiceid, async: false, successCallback: function (result) { invoice = result; }, errorCallback: function (error) { for(var propertyName in error) { alert(error[propertyName]);} } });
Next we need to get all the related invoice products. Here we’re using the OData filtering methods. What we get back is an array of invoice products.
var invoiceGuid = invoiceid.replace("{","'").replace("}","'"); var filterQuery = "?$filter=InvoiceId/Id eq guid" + invoiceGuid; rmSvcToolkit.retrieveMultiple({ entityName: "InvoiceDetail", async: false, odataQuery: filterQuery, successCallback: function (result) { products = result; }, errorCallback: function (error) { for(var propertyName in error) { alert(error[propertyName]);} } );
Now we have invoice and related products and we can start copying them. In this portion of the code we are mapping the attributes of the invoice we retrived to the attributes of new invoice entity. Important notice: We’re using the attribute SchemaName so be careful regarding the casing. So choose the attributes you want to copy and map them. If the copying of the invoice is successful we need to copy all of the related products. I created a function that copies the product called copyInvoiceProduct(). When all of the invoice products are copied we open a new window that opens up this new invoice.
var today = new Date(); XrmSvcToolkit.createRecord({ entityName: "Invoice", entity: {Name: invoice.Name, CustomerId: invoice.CustomerId, PriceLevelId: invoice.PriceLevelId, TransactionCurrencyId: invoice.TransactionCurrencyId, BillTo_City: invoice.BillTo_City, BillTo_Country: invoice.BillTo_Country, BillTo_PostalCode: invoice.BillTo_PostalCode, BillTo_Line1: invoice.BillTo_Line1, ShipTo_Country: invoice.ShipTo_Country, ShipTo_Line1: invoice.ShipTo_Line1, ShipTo_PostalCode: invoice.ShipTo_PostalCode, ShipTo_City: invoice.ShipTo_City, DateDelivered: today }, async: false, successCallback: function (result) { for (i=0;i<products.length;i++) { copyInvoiceProduct(products[i],result); } window.open(serverUrl + "/main.aspx?etn=invoice&pagetype=entityrecord&id=%7b" + result.InvoiceId + "%7d",'_blank'); }, errorCallback: function (error) { for(var propertyName in error) { alert(error[propertyName]);} } });
function copyInvoiceProduct(product, invoice) { XrmSvcToolkit.createRecord({ entityName: "InvoiceDetail", entity: { InvoiceId: {__metadata: { type: "Microsoft.Crm.Sdk.Data.Services.EntityReference" }, Id: invoice.InvoiceId,LogicalName: "invoice" }, ProductDescription: product.ProductDescription, Quantity: product.Quantity, PricePerUnit: product.PricePerUnit, Tax: product.Tax, IsProductOveridden: product.IsProductOveridden, ManualDiscountAmount: product.ManualDiscountAmount }, async: false, successCallback: function (result) { }, errorCallback: function (error) { for(var propertyName in error) { alert(error[propertyName]);} } }); }
And that’s it. Simple isn’t it? :) Here is the complete function: copyInvoice.js
I know it looks complicated but once you get a hang of it you’ll be faster and more creative.
I didn’t go into all of the details since it would take me another page or two to have everything covered. So if you have any questions feel free to ask.
Below is the URL -
Wednesday, February 11, 2015
How to read fetch xml from app.config
Today i learnt about CDATA that can be used to read fetch xml in our .NET code as described below-
<FetchXML>
<![CDATA[<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="account">
<attribute name="accountid" />
<filter type="and">
<condition attribute="name" operator="eq" value="test3443" />
</filter>
</entity>
</fetch>]]>
</FetchXML>
<FetchXML>
<![CDATA[<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="account">
<attribute name="accountid" />
<filter type="and">
<condition attribute="name" operator="eq" value="test3443" />
</filter>
</entity>
</fetch>]]>
</FetchXML>
Tuesday, February 10, 2015
Saturday, February 7, 2015
Wednesday, February 4, 2015
CRM 2011 : Retrieve related entity data
I was working on requirement of retrieving users for a particular team. Team & users are N:N related. It is a simple code where you need to specify the relationship name between two entities: Below is the Url.
http://mscrmkb.blogspot.com/2010/12/crm-2011-retrieve-related-entity-data.html
http://mscrmkb.blogspot.com/2010/12/crm-2011-retrieve-related-entity-data.html
Could not load file or assembly 'Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified
This error occurs because no Identity Framework is installed on your m/c. Please install Identity framework from below site.
Tuesday, February 3, 2015
Getting data from temorary table in SQL Server
use northwind exec('select top 5 orderId, customerId into #temp from orders; select * from #temp') select * from #temp drop table #tempThe #temp table gets created and queried once as seen in results panel.
Then we have an error message in messages panel informing us:
Invalid object name '#temp'.
Subscribe to:
Posts (Atom)