Category Archives: Development

My development efforts ( mostly Microsoft .NET )

What exactly is a minimum viable product (MVP)?

Earlier this month I gave a talk to several groups of 8th graders at a middle school where a friend of mine teaches.  I talked to them about what it was like to be a software developer, what I did, what my day was like, and what they would need to do if they chose such a path.

Beside just rambling on about the processes of learning and practice, I also showed them a Xamarin Forms application called About Time that I had written quite a while ago that is a quirky kind of clock.  I showed them a bit of the code the deployed and ran it on my iPad – which was about the coolest thing I could think of related to some of the normal development that I do which most 8th-graders would not be too enthused about.  But creating an app that you could put on your iPad, that was something else.

The app was just a learning experience for me when I was learning Xamarin Forms and while I have considered it many times, I have not spent any actual time to finish it up to publish the Apple App Store.

A couple of days ago I get a package in the mail that contained a stack of thank you cards from the school and from the students themselves. Most were of the “thanks for coming and talking to us” variety but a few were very appreciative and inventive in what they had to say and you could tell they actually took the time to write what they thought.

One young lady named Alexis, had this to say:

I really think you should make that software into an app. The only advice is if you could change the background so it could be personal to users.

Does she look like a future product manager or what?

So Alexis, let’s discuss the application development process and what it actually means to ship an application that people will either purchase or use.

Minimum Viable Product (MVP)

A minimum viable product, is exactly that: It is a version of your product that contains the minimum amount of functionality required to perform its job.

The purpose of an MVP is to get it into the hands of the users.

Why is this so important? Back in the late 1800’s, a Prussian army general named Helmuth von Moltke the Elder coined phrase (paraphrased):

No plan survives contact with the enemy.

My updated version of this is:

No plan survives contact with reality.

The basic concept is that we as software developers do not often know what our user want.  In fact, in most cases, the users themselves do not know what they want.  So what do we do?  We guess.  We may shroud this in market research, focus groups, one-on-one’s, etc., but in reality, until people see a thing, it’s really just conjecture.

Don’t believe me, listen to these guys:

SteveJobQuote-1

HenryFordFasterHorses

So, the idea is that you create a product, get it into the hands of users, the take their feedback to improve the product.

So you want me to ship crap?

Nope. That is not what I said. Your product needs to be professional, functional, and perform the basic tasks for which it was designed.  But, it does not need to have a million bells and whistles that people may or may not use or need.

Consider this:

Embarrased

Pretty scary to think about, but unfortunately it contains a lot of truth.

Why is this important?

It all comes back to time and money. It serves no useful purpose to spend months and thousands of dollars adding features that you are not sure people will need or want.  Stick with the basics then iterate from there. It costs less to add a feature than it does to change a feature once implemented.

It is also important to note that this practice is valuable no matter of this is a paid application or a free application. Time is still money and they don’t make any more than they used to.

I have personally spent months developing software that people did not buy and let me tell you, the thought of the sunk cost of that effort makes me queasy every time I think about it.

So let’s get back to the application that I demonstrated to the kids as an example of an MVP, the possible update process, and how things can work.

About Time, now and future

The idea for About Time came from a watch that I read about ten years ago. I thought it was a pretty cool idea but later research seems to indicate that the watch never made it out of the prototype phase.

My version is slightly different, but it uses the same concept of telling time using phrases that many people commonly use when telling someone the time of day.

Here are some screen shots showing About Time in action:

NightImage

At around 5:35am.

SunriseImage

6:10am or so.

Here is the path that About Time could take:

About Time v1.0

Version 1.0 is my MVP and really needs to have just the basic functionality:

  • Display the time
  • Change the background image to reflect the time of day
  • Update the time should the application be running on the screen so we always have the current time

At this point, it does everything that it is designed to do so I can bundle it up and ship it to the app store. My main decision point now is the price. Here are my options:

  • Give it away
  • Charge $.99

Remember that on the Apple App Store, once a user purchases your application, they get future updates for free. The only way to charge for an update is to create a new application. So once you have their $.99 (minus the Apple commission), you have made all of the money that you can make off of the sale to that customer, unless you create a new app. (we’ll discuss other monetary options in a minute.)

About Time v1.1

Assuming people are using the application and that you have received good feedback and ratings, consider moving the the Android and Windows Phone market places. Since the code was written in Xamarin Forms, it is already cross-platform and just needs to be published to those markets.

About Time v1.2

The phrases are hard-coded into the application so let’s make them configurable by the user. Let’s also ship some pre-defined phrases from other countries.

About Time v1.3

The times that the app uses to determine when to switch background images is hard-coded. Let’s add a call to an Internet web service that will get the location of the device to determine the proper boundaries for sunrise, sunset, day, and night.

About Time v1.4

Apple Watch edition. Assuming by this time that Apple actually allows us to create applications that can show you the time of day.

About Time v1.5

At this point we need to discuss and discover if we need to add an alarm clock feature. If so, then do it now.  How do we know if we need an alarm feature? We ask our users to see if that would be something they would find useful.

About Time v2.0

In this version, we add the following features available as in-app purchases:

  • The ability to add custom background images
  • Synchronizing configuration data (phrases, images, etc.) with a cloud-based database so that About Time running on any of the user’s devices will always have the same configuration.

This will be the final version of About Time, baring any maintenance updates.

So what is the point of the multiple versions?

Well, there are several things to consider:

1. Regular updates will increase your visibility within the App Store.

2. You have a methodology that allows for a constant feedback loop with your customers.

3. You don’t have to worry about spending time on unused features should the feedback not be favorable. You can halt development at any point in this product plan, should you choose.

#3 is the most important point, so let me give you an example of why:

In v2.0 we are going to allow the users to select a background photo of their own.  While this seems like a trivial exercise, here are the actual steps required:

  1. Selection of the photo for each of the four times of day
  2. Storage of the photos
  3. Photo analysis. In order to have a text color that allows the time-phrase to be shown, we have to sample to photo to determine what the main color is so that we can properly adjust the phrase to be either black or white.

Again, not rocket-science steps here, but things that must be considered and which will take time to do properly.

In Conclusion

So to wrap things up, just ship the damn thing. You can make it perfect later.

Dynamics CRM Code Snippet: Get Maximum Attachment Size Limit

If you are ever working with any code that needs to upload attachments to Dynamics CRM, you may run into an issue where your attachment is larger than the default of 5MB.

To prevent errors from occurring, you can check the Organizational Setting for the maximum upload file size, using code like this:

public int GetAttachmentSizeLimit()
{
    var size = -1;

    var query = new QueryExpression("organization")
    {
        ColumnSet = new ColumnSet("maxuploadfilesize")
    };

    var results = _proxy.RetrieveMultiple(query);

    if (results.Entities.Count == 0)
    {
        return size;
    }

    return results[0].GetAttributeValue<int>("maxuploadfilesize");
}

Depending on how you are uploading the attachment, you can either present the user with a warning (if uploading a single attachment), or ignore the attachment and add it to an exception report if running in a batch operation.

This setting can be found under Settings, Administration, System Settings, on the Email tab:

system settings - file upload size

Dynamics CRM Developer Tip O’ the Day: Does the user have a security role?

Occasionally, when working with .NET and the Dynamics CRM SDK, you’ll find a need to see if the current user has a particular security role. This is helpful before performing any operation that may result in a security exception.

Here is a small method to handle that call:

public bool UserHasSecurityRole(OrganizationService service, string securityRoleName)
{
    var whoami = (WhoAmIResponse)service.Execute(new WhoAmIRequest());

    using (var context = new CrmOrganizationServiceContext(service))
    {
        var query = (from sr in context.CreateQuery<SystemUserRoles>()
                        join r in context.CreateQuery<Role>() 
                           on sr.RoleId.Value equals r.RoleId.Value
                        where r.Name == securityRoleName
                        where sr.SystemUserId == whoami.UserId
                        select sr).ToList();

        return query.Count > 0;
    }
}

and it is used like this:

var isSystemAdministrator = UserHasSecurityRole(service, "System Administrator");

How It Works

We first perform a WhoAmI request to get the ID of the current user.

Next, the method uses the Dynamics CRM LINQ (Language Integrated Query) provider to query the SystemUserRole and Role entities where the Role Name is equal to the security role and the User ID is that of the current user.

If there are any records returned at all, then that user has the security role we were looking for.

A true or false is returned depending on that number.

Notes from the Field: Working with Dynamics CRM Dialogs

Here are a few notes I’ve collected while working with a Dynamics CRM project involving dialog processes:

Required Fields

There is not a feature to allow a dialog field to be required, Dynamics CRM Tip of the Day, Tip #391: Mandatory fields in dialogs discusses a possible work around.

Float Data Field

One of the possible data entry types is a floating point number, which has five decimal places to the right of the period. Unfortunately, there is no way to change the number of decimal places, so we’re stuck with showing the users all five.

Option Set Field

OptionSets have a few things going for them that make them fairly cool to use, but also have some restrictions.

  • An OptionSet can be displayed as a normal drop-down or a series of radio buttons.
  • There seems to be no way to extract the contents of an existing OptionSet field for inclusion in a dialog so you must manually replicate the contents of the existing OptionSet field. This makes for a secondary maintenance point should you need to change or alter the values of an OptionSet.
  • There is no default value for an OptionSet. Instead, just put the default value at the top of the list.
  • The final thing that is rather cool about OptionSets is the ability to define, and later use, a CRM Data Query to extract a list using a FetchXml query.

Two Option Field

There is no Two Option field, so you will need to create your own using a OptionSet, formatted as either a drop-down or two radio buttons.

Lookup Field

Configuring a lookup field can be a little bit different than you expect. Read Leon Tribe’s article: Dialog Lookup Values For Common Entities for more information and some pointers.

Field Tips

Each field has a Tip property associated with it which will allow you to display additional information or instructions to the user. I can see this coming in handy.

Page Names

Even though we have the ability to break our dialog into pages, and give each page a name, that name doesn’t seem to display anywhere. But it would be nice if it did.

 

Conclusion

I have honestly avoided dialogs because they seemed so rudimentary and also do not seem to have been shown much love since they were released in Dynamics CRM 2011, but they do have their uses and if the CRM team could carve out a little time to correct some of the shortcomings, then they would prove quite useful.  Until then there are always third-parties like TK Dialogs.

How to capture moble login errors with Azure ADAL

If you are creating a mobile application that uses the Azure Active Directory Access Library (ADAL) component, you will need to learn how and when errors are returned from the component.

Here are a few to get you started:

Error Messages

#1: User presses the cancel button on the first login page

If the user presses the Cancel button on this page:

image

You will receive the following error message:

User canceled authentication

 

#2: User presses the cancel button on the permissions page

If the user presses the Cancel button on this page:

SNAGHTML58b2d43

You will receive an error message that starts with:

aadsts65004: the resource owner or authorization server denied the request.

 

#3: User supplies incorrect URL

If you are creating an application that accesses Dynamics CRM Online, you must pass in the URL for the organization you wish to connect to. If the URL supplied to the ADAL service is incorrect, you will receive the following error message:

Error: nameresolutionfailure

 

#4: ADAL component cannot display the login page

The way the ADAL component works is by displaying a webview control in which is loads the web-based login pages to request user credentials using OAuth. If the ADAL component has an issue displaying that webview, you will receive the following error:

authentication_ui_failed: the browser based authentication dialog failed to complete

 

Handling Errors

Some of these errors need to be reported back to the user an an error and some do not.

#1 and #2 simply mean the user has canceled the operation and you can probably safely ignore the error and cancel the login operation.  But keep in mind that #2 could also generate a real error but I am not exactly sure of the circumstances where that would occur.

#3 is an error that needs to be displayed to the user, but I would change the actual message to something that makes more sense to the user.

In my case, I was asking for the organization name and the Dynamics CRM Data Center (crm, crm2, crm4, etc.) and if the user entered or selected the wrong information, then I wanted to tell them it was incorrect instead of showing the nameresolutionfailure message.

#4 in all likelihood means that your application is not passing the proper window handle to the control (RootViewController in the case of iOS) which is a condition you need to correct or your app will never work with ADAL correctly.

Want to learn more about Developing Dynamics Plugins?

Hi Everyone,

My Dynamics CRM Deep Dive: Plugins book is in development and is available for pre-release purchase.

The book should be available in late October, but if you purchase now, you’ll receive:

  1. A $20 discount on the final e-book.
  2. The electronic resources that will accompany the book (as they are completed).
  3. Advanced copies of chapters for your own education as well as to provide feedback, should you feel so inclined.

Let me know if you have any questions or suggestions.

Thanks, Mitch

Dynamics CRM JavaScript: Ensure the current record is Saved before opening a new record

In yesterday’s post, Tracing the Dynamics CRM Form Data save Operation, I discussed identifying data that would be sent to the database when you saved a Dynamics CRM record.

Part of the issue I was facing was related to the form not completing the save operation before I opened a new form record. Since Dynamics CRM 2013 and 2015 user interface rarely opens a new window, the user was getting a warning that there was unsaved data when the page was being switched between the Account and, in this case, the Opportunity record.

So to prevent this from happening, I had to wait for the save to complete then open the new Opportunity record.

Here is the code where I do that:

……

var parameters = { customer_id: customerId, customer_name: customerName, customer_type: customerType, name: Xrm.Page.getAttribute("name").getValue() }; var windowOptions = { openInNewWindow: true }; setTimeout(function () { openNewForm(parameters); }, 2000); } function openNewForm(parameters) { var windowOptions = { openInNewWindow: true }; Xrm.Utility.openEntityForm("opportunity", null, parameters, windowOptions); }

The Details

We are using the JavaScript method setTimeout to wait for 2,000 milliseconds (2 seconds for those of us who are not computers).

At the end of the wait period, we will call a function called openNewForm which will use the Dynamics CRM method Xrm.Utility.openEntityForm, which opens a new Opportunity form.

You may need to adjust the wait period up or down, depending on your environment. The idea is to wait long enough for the save to complete, but not long enough to irritate the user.

Tracing the Dynamics CRM Form Data save Operation

I ran into an interesting issue this week where I was opening up a new Opportunity Entity form from the Account record via a Command Bar button.

The issue was that I was getting a warning that I was about to discard changes to the record I was leaving, and would l like to Continue or Cancel.

I verified all of the JavaScript and found that I was not performing any updates on form load, so there must be something else happening to modify a value on the form so that CRM thinks the record needs to be saved.

But what was being changed?

I thought about it for a bit and realized I could have CRM tell me what was happening, though a little known or used method called Xrm.Page.data.entity.getDataXml.

Dynamics CRM, by and large, does not send unaltered data to the database when a Save occurs. By using getDataXml, I can see exactly which field or fields were being modified.

Implementation

To implement this code, I created a small onSave method:

function onSave() {
    alert(Xrm.Page.data.entity.getDataXml());
}

Then created an OnSave event on the form properties:

image

Note: Make sure this event is the final OnSave event to ensure that all of the other OnSave events have completed.

This will display a message box showing what fields have been changed and the new values. Basically the same thing that will be sent to the database.

Resolution

It turns out that in this particular case, I was programmatically:

  1. Setting a field value
  2. Saving the record
  3. Opening a new related record

The issue was that the Save operation had not completed so the field I had modified was still considered dirty.

This allowed me to correct that issue and now things work as expected.

Up Next

My next article will outline how to work around the saving of the record while navigating to another record issue I ran into above.

Dynamics CRM JavaScript: Working with Tabs and Sections-The right way

Back in March I wrote the following article: Dynamics CRM Developer Tip O’ the Day: Working with Tabs.

Today I was working on some pre-existing customer JavaScript and ran across this:

Xrm.Page.ui.tabs.get(0).sections.get(6).setVisible(false); // Hide section

Don’t ever do that! Bad developer! Bad!

So again, never, ever use the numerical value to reference a Tab or a Section, unless you have no choice. 

We don’t want to use numbers because if you move or add Tabs or Sections, your code will no longer work.

Do this instead:

Xrm.Page.ui.tabs.get("General").sections.get("Details").setVisible(false); // Hide section

As long as your Tabs and Sections are named properly, you should not have an issue.