Sunday, April 26, 2015

Entity Framework Associated Table Counts with One Query

With the Entity Framework, we are able to use navigation properties to get the associated records. This however loads all the child rows for a parent. In some instances, we may just need to get a count of the associated records without having to load all the rows. For this case, we can use the company navigation properties to get that counts and use a company view-model class to hold the additional information. Let’s see more detail how this can be done.

We start by first looking at our database entities:




We can see, the Company entity has the associations to the Department and Employee entities. We want to be able to return the Company information with associated counts using this model:


To load the company information with count records, we can use the following snippet:

    using (var context = new Entities())
           {
               companies = context.Companies.Select(c => new model.Company()
               {
                   Name = c.Name,
                   Id = c.Id,
                   EmployeeCount = c.Employees.Count(),
                   DepartmentCount = c.Departments.Count()
               }).ToList();
           }

With this lambda expression, we are using the navigation properties to get the count of the associated records. The query that is created and sent to the database is just one SQL statement that gets the company data and the associated rows count of the other tables.  The result is returned as a list of CompanyModel objects.

With this approach, we no longer need to load all the rows from the other tables using the navigation properties. This should  improve the overall performance of the application.

I hope you find this tip useful.


Saturday, April 4, 2015

Control Angularjs Modal Dialog Width

Note: This solution requires Bootstrap 3.1.0 or later.

With AngularJs, we can open a modal dialog by using the $modal service. The script to open a custom modal dialog can be as simple as follows:

var modalInstance = $modal.open({
                templateUrl: ‘my-dialog’,                               
                replace: true,
                                          
            });

This AngularJS service is based on the Boootstrap’s markup and CSS definitions. The templateUrl is just a HTML template emedded on the page, and it is listed below:

<script type="text/ng-template" id="my-dialog">
    <div>
        <div class="modal-header">
            <h3>Hi Dialog by ozkary</h3>
        </div>
        <div class="modal-body">
            <table width="780px;"><tr><td>Hello by ozkary.com</td></tr></table>
        </div>
        <div class="modal-footer">
            <button class="btn btn-primary" ng-click="$dismiss()">OK</button>
            <button class="btn btn-warning" ng-click="$close()">Cancel</button>
        </div>
    </div>
</script>

This script listed above uses the default value which renders a modal dialog with a mid size frame which is mostly use for messaging. When we need control of the width, we can use the parameter size. This parameter is a bit miss-leading since it may make us think that we can enter an integer value and control the size.  Instead, we need to use string constants that is appended to the CSS class modal-?? where the ?? is replaced by the string parameter. 

When we want a bigger dialog, we can use the lg constant, so that the style is set to modal-lg as follows:

var modalInstance = $modal.open({
                templateUrl: 'my-dialog',                               
                replace: true,
                size:'lg'                          
            });

We can see this in action with this plunker. The dialog is small or large based on the size parameter that we provide.




Hope it helps.

Sunday, March 1, 2015

AngularJS ng-model Concatenate Model Values

In order to concatenate a model property values in AngularJS, we need to create an extended property on the model and then use that property with the ng-model directive.

We can illustrate this with the following angular module:

        var app = angular.module('app', []);
        app.controller('AppCtrl', function ($scope) {
            $scope.customer = {name: 'ozkary', id: '1234' };


            $scope.allInfo = function (data) {
                return data.id + ' - ' + data.name;
            }
        });

On the view, we attempt the following:

<body ng-app="app" ng-controller="AppCtrl">

    <h2>Extended Property - ng-init expression</h2>   
    <h2>Bad - ng-model does not like expressions</h2>
    <input type="text" ng-model="allInfo(customer)">
</body>

The ng-model can only accept a property for a two way binding, so it does not work with an expression. Therefore, we need to create another property that meets our desired results. In order to do that, we can use the ng-init directive to evaluate an expression and assign the result to another property, info. We can then use ng-model directive with this new property. We can demonstrate this as follows:

    <input type="text" ng-model="customer.info"
               ng-init="customer.info = allInfo(customer)">

To view this in action visit this plunk:  http://plnkr.co/edit/PSvwCE?p=info
We can now take a complex model and create extended properties to easily bind it to the view.


Hope this tip is useful.

Active Directory SetPassword or ChangePassword Possible Errors


When we try to update the user password on an Active Directory (AD or AD LDS) environment, we usually read on the documentation about the basic steps that are needed to set or change the password to a user. If we follow those basic steps, we would be very lucky of all goes well. There are often several other requirements that need to be met to be able to set the password. Some of these include:
  • The password is clear text
  • The password does not meet the minimum length requirement
  • The password does not meet the format like numeric or capitalization (password policy)
  • The Authentication type is not high enough


Any of these items can cause COM error similar to this:

“Exception has been thrown by the target of an invocation COMException (0x80005009)

To illustrate this, let’s looks at what the basic code to set a user’s password looks like:

//add these setting on the configuration file
        const string adConnection = "ldap://mydomain:389/CN=ORG,DC=OZKARY,DC=COM";
        const string adUsername = "someuser"//user with admin access to AD
        const string adPw = "*******";

        public static void SetPassword(string username, string password){

            using (var de = new DirectoryEntry(adConnection, adUsername, adPw))
            {
                //Win Server 2008 schema or less uses sAMAccount 
                //not userPrincipalName
         string query = String.Format("(&(objectCategory=person)(objectClass=user)(&(userPrincipalName={0}))", username);           

                using (var ds = new DirectorySearcher(de))
                {
                    ds.SearchScope = System.DirectoryServices.SearchScope.Subtree;
                    ds.Filter = query;
                    SearchResult res = ds.FindOne();

                    if (res != null)
                    {
                        using (var userEntry = res.GetDirectoryEntry())
                        {
                            userEntry.Invoke("SetPassword", new object[] 
                            { password });                           
                        }

                    }
                }
            }
        }

If we are lucky, this should be enough for the operation to be successful, but we are probably getting errors, and that is why we are writing this article.  The first thing to do is to figure out more about this error by extracting the COM exception error code and mapping them to a root cause. This is where this function may be helpful:

  private static void HandleComException(TargetInvocationException tie)
        {          
            if (tie != null && tie.InnerException is COMException)
            {
                COMException ce = (COMException)tie.InnerException;
                int errorCode = ce.ErrorCode;
                string message = ce.Message;
                message += ce.InnerException != null ? ce.InnerException.Message :                                      string.Empty;
                Trace.TraceError(message);
                message = "error_not_found";

                // if the exception is due to password not meeting complexity requirements, 
                // then return ProviderException                
                if ((errorCode == unchecked((int)0x800708c5)) 
                    || (errorCode == unchecked((int)0x8007202f)) 
                    || (errorCode == unchecked((int)0x8007052d)) 
                    || (errorCode == unchecked((int)0x8007052f)))
                {
                    message = "password_not_complex";//password policy not met
                }
                else if ((errorCode == unchecked((int)0x8000500d)))
                {
                    message = "no_secure_conn_for_password";//need higher than security=None
                }
                else if ((errorCode == unchecked((int)0x80072035)))
                {
                    //min password length or the password was used before
                    //The server is unwilling to process the request
                    message="min_password_length_used_before";
                }
                else if ((errorCode == unchecked((int)0x80005009)))
                {
                    message="password_clear_text";//need to set option to use clear text
                }

                throw new ProviderException(message, ce);//need to match this error
            }
           
        }
The exception usually is of type TargetInvocation, but it does not provide enough information to determine the problem. The function reads the inner exception (COM exception) and compare the error code to the possible Active Directory Service Interface (ADSI) errors. As you can see there are other possible errors, but we are only handling the ones that are specific to a password change.

The password not complex and the password length errors basically mean that the new password is not meeting the password policy requirements. We need to find out about those requirements from the AD administrator and make sure that the new password meets them before we attempt to set the password.

Some requirements may be:
  • Length of 8 characters
  • At least one capital letter or number
  • One special character

The unsecured connection and clear text errors can be addressed by making modifications to the code. When we try to update the password for a user, we can’t use a Secure=None level on the authentication type. We need to provide a higher security by adding these lines of code:

AuthenticationTypes auth = AuthenticationTypes.Signing | AuthenticationTypes.Sealing 
AuthenticationTypes.Secure;          
using (var de = new DirectoryEntry(adConnection, adUsername, adPw,auth))

The clear text error is due to the fact that the password is not hashed, so we need to indicate that this would be OK by adding this line of code:

userEntry.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingClear;

Once we have added those changes, the password update should be able to work or we should at least have added instrumentation in our code to help identified the problem. This is what the new version looks like:

public static void SetPasswordNew(string username, string password)
        {
            AuthenticationTypes auth = AuthenticationTypes.Signing |  AuthenticationTypes.Sealing                                        | AuthenticationTypes.Secure;          
            using (var de = new DirectoryEntry(adConnection, adUsername, adPw,auth))
            {
                //Win Server 2008 schema or less uses sAMAccount not userPrincipalName. 
                //add to config file
string query = String.Format("(&(objectClass=user)(&(userPrincipalName={0}))",                      username);           

                using (var ds = new DirectorySearcher(de))
                {
                    ds.SearchScope = System.DirectoryServices.SearchScope.Subtree;
                    ds.Filter = query;
                    SearchResult res = ds.FindOne();

                    if (res != null)
                    {
                        try
                        {
                            using (var userEntry = res.GetDirectoryEntry())
                            {
                                userEntry.Options.PasswordEncoding =                                 PasswordEncodingMethod.PasswordEncodingClear;    
                                userEntry.Invoke("SetPassword", new object[] { password });
                            }
                        }
                        catch (TargetInvocationException tie)
                        {
                            HandleComException(tie);
                        }
                        catch (Exception ex)
                        {
                            Trace.TraceError(ex.Message); //handle this other error
                          
                        }
                       
                    }
                }
            }
        }


By adding these modifications, the password update should be successful.

Hope this helps and thanks.