Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

A Sample Silverlight 4 Application Using MEF, MVVM, and WCF RIA Services - Part 1

, 7 Jul 2011 CPOL
Part 1 of a series describing the creation of a Silverlight business application using MEF, MVVM Light, and WCF RIA Services.
IssueVisionforSilverlightSetup20110630.zip
IssueVision for Silverlight Setup
IssueVisionSetup.msi
setup.exe
IssueVision_for_Silverlight_setup.zip
IssueVisionSetup.msi
setup.exe
IssueVision_Silverlight20110630.zip
IssueVision.Silverlight
Assemblies
GalaSoft.MvvmLight.Extras.SL4.dll
GalaSoft.MvvmLight.SL4.dll
MenuControls.dll
System.Windows.Controls.DataVisualization.Toolkit.dll
System.Windows.Controls.Toolkit.dll
System.Windows.Controls.Toolkit.Internals.dll
System.Windows.Interactivity.dll
Database
IssueVision.Client
IssueVision.Client.csproj.user
Assets
Signin.png
Controls
Properties
Views
IssueVision.Common
IssueVision.Common.csproj.user
Controls
Helpers
Models
Properties
Resources
Themes
IssueVision.Data
IssueVision.Data.csproj.user
Properties
Web
Resources
IssueVision.Data.Web
IssueVision.edmx
Helpers
MetadataClasses
Properties
Resources
Services
IssueVision.Model
IssueVision.Model.csproj.user
Properties
IssueVision.Setup
IssueVisionSetup.vdproj
IssueVision.ViewModel
IssueVision.ViewModel.csproj.user
Properties
IssueVision.Web
IssueVision.Web.csproj.user
Properties
IssueVision_Silverlight_20110316.zip
GalaSoft.MvvmLight.Extras.SL4.dll
GalaSoft.MvvmLight.SL4.dll
MenuControls.dll
System.Windows.Controls.DataVisualization.Toolkit.dll
System.Windows.Controls.Toolkit.dll
System.Windows.Controls.Toolkit.Internals.dll
System.Windows.Interactivity.dll
IssueVision.Client.csproj.user
Signin.png
DataSources
IssueVision.Data.Web.IssueVisionContext.datasource
IssueVision.Data.Web.PasswordResetContext.datasource
IssueVision.Common.csproj.user
IssueVision.Data.csproj.user
IssueVision.edmx
DataTypes
IssueVision.Model.csproj.user
IssueVisionSetup.vdproj
IssueVision.ViewModel.csproj.user
IssueVision.Web.csproj.user
IssueVision_Silverlight_20110606.zip
GalaSoft.MvvmLight.Extras.SL4.dll
GalaSoft.MvvmLight.SL4.dll
MenuControls.dll
System.Windows.Controls.DataVisualization.Toolkit.dll
System.Windows.Controls.Toolkit.dll
System.Windows.Controls.Toolkit.Internals.dll
System.Windows.Interactivity.dll
IssueVision.Client.csproj.user
Signin.png
IssueVision.Common.csproj.user
IssueVision.Data.csproj.user
IssueVision.edmx
IssueVision.Model.csproj.user
IssueVisionSetup.vdproj
IssueVision.ViewModel.csproj.user
IssueVision.Web.csproj.user
IssueVision_Silverlight_Setup.zip
IssueVisionSetup.msi
setup.exe

namespace IssueVision.Data.Web
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Data;
    using System.Linq;
    using System.ServiceModel.DomainServices.EntityFramework;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;


    // Implements application logic using the IssueVisionEntities context.
    // TODO: Add your application logic to these methods or in additional methods.
    // TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access
    // Also consider adding roles to restrict access as appropriate.
    [EnableClientAccess()]
    [RequiresAuthentication]
    public class IssueVisionService : LinqToEntitiesDomainService<IssueVisionEntities>
    {

        public IQueryable<Attribute> GetAttributes()
        {
            return this.ObjectContext.Attributes;
        }

        public void InsertAttribute(Attribute attribute)
        {
            attribute.ID = Guid.NewGuid();

            if ((attribute.EntityState != EntityState.Detached))
            {
                this.ObjectContext.ObjectStateManager.ChangeObjectState(attribute, EntityState.Added);
            }
            else
            {
                this.ObjectContext.Attributes.AddObject(attribute);
            }
        }

        public void UpdateAttribute(Attribute currentAttribute)
        {
            this.ObjectContext.Attributes.AttachAsModified(currentAttribute, this.ChangeSet.GetOriginal(currentAttribute));
        }

        public void DeleteAttribute(Attribute attribute)
        {
            if ((attribute.EntityState == EntityState.Detached))
            {
                this.ObjectContext.Attributes.Attach(attribute);
            }
            this.ObjectContext.Attributes.DeleteObject(attribute);
        }

        public IQueryable<File> GetFiles()
        {
            return this.ObjectContext.Files;
        }

        public void InsertFile(File file)
        {
            if ((file.EntityState != EntityState.Detached))
            {
                this.ObjectContext.ObjectStateManager.ChangeObjectState(file, EntityState.Added);
            }
            else
            {
                this.ObjectContext.Files.AddObject(file);
            }
        }

        public void UpdateFile(File currentFile)
        {
            this.ObjectContext.Files.AttachAsModified(currentFile, this.ChangeSet.GetOriginal(currentFile));
        }

        public void DeleteFile(File file)
        {
            if ((file.EntityState == EntityState.Detached))
            {
                this.ObjectContext.Files.Attach(file);
            }
            this.ObjectContext.Files.DeleteObject(file);
        }

        /// <summary>
        /// Get all issues orderby by StatusID, and Priority
        /// </summary>
        /// <returns></returns>
        public IQueryable<Issue> GetIssues()
        {
            return this.ObjectContext.Issues
                .Include("Files").Include("Attributes")
                .OrderBy(g => g.StatusID).ThenBy(g => g.Priority);
        }

        /// <summary>
        /// Get all un-resolved issues
        /// </summary>
        /// <returns></returns>
        public IQueryable<Issue> GetAllUnResolvedIssues()
        {
            return this.ObjectContext.Issues
                .Where(n => (n.ResolutionID == null || n.ResolutionID == 0));
        }

        /// <summary>
        /// Get all issues for the current user orderby by StatusID, and Priority
        /// </summary>
        /// <returns></returns>
        public IQueryable<Issue> GetMyIssues()
        {
            return this.ObjectContext.Issues
                .Include("Files").Include("Attributes")
                .Where(g => g.AssignedToID.Equals(this.ServiceContext.User.Identity.Name))
                .OrderBy(g => g.StatusID).ThenBy(g => g.Priority);
        }

        /// <summary>
        /// When a new issue is created, we should check the following:
        /// 
        /// 1) OpenedDate, OpenedByID, LastChange, changedByID, and IssueID
        ///    should be set with their initial values.
        /// 2) If status is Open, the AssignToID should be null.
        /// 3) ResolutionDate and ResolvedByID should be set based on ResolutionID.
        /// </summary>
        /// <param name="issue"></param>
        public void InsertIssue(Issue issue)
        {
            issue.OpenedDate = DateTime.Now;
            issue.OpenedByID = this.ServiceContext.User.Identity.Name;
            issue.LastChange = DateTime.Now;
            issue.ChangedByID = this.ServiceContext.User.Identity.Name;
            // create a new Issue ID
            issue.IssueID = ObjectContext.Issues.Count() > 0 ? (from iss in ObjectContext.Issues select iss.IssueID).Max() + 1 : 1;
            // if status is Open, AssignedToID should be null
            if (issue.StatusID == IssueVisionServiceConstant.OpenStatusID)
            {
                issue.AssignedToID = null;
            }
            // set ResolutionDate and ResolvedByID based on ResolutionID
            if (issue.ResolutionID == null || issue.ResolutionID == 0)
            {
                issue.ResolutionDate = null;
                issue.ResolvedByID = null;
            }
            else
            {
                issue.ResolutionDate = DateTime.Now;
                issue.ResolvedByID = this.ServiceContext.User.Identity.Name;
            }

            if ((issue.EntityState != EntityState.Detached))
            {
                this.ObjectContext.ObjectStateManager.ChangeObjectState(issue, EntityState.Added);
            }
            else
            {
                this.ObjectContext.Issues.AddObject(issue);
            }
        }

        /// <summary>
        /// When a new issue is updated, we should check the following:
        /// 
        /// 1) LastChange and changedByID should be upated.
        /// 2) If status is Open, the AssignToID should be null.
        /// 3) ResolutionDate and ResolvedByID should be set based on ResolutionID.
        /// </summary>
        /// <param name="issue"></param>
        public void UpdateIssue(Issue issue)
        {
            // Business logic:
            // Admin user can read/update any issue, and
            // normal user can only read/update issues assigned to them
            // or issues created by them and have not assigned to anyone.
            if (!IssueIsReadOnly(this.ChangeSet.GetOriginal(issue)))
            {
                issue.LastChange = DateTime.Now;
                issue.ChangedByID = this.ServiceContext.User.Identity.Name;
                // if status is Open, AssignedToID should be null
                if (issue.StatusID == IssueVisionServiceConstant.OpenStatusID)
                {
                    issue.AssignedToID = null;
                }
                // set ResolutionDate and ResolvedByID based on ResolutionID
                if (issue.ResolutionID == null || issue.ResolutionID == 0)
                {
                    issue.ResolutionDate = null;
                    issue.ResolvedByID = null;
                }
                else
                {
                    issue.ResolutionDate = DateTime.Now;
                    issue.ResolvedByID = this.ServiceContext.User.Identity.Name;
                }

                this.ObjectContext.Issues.AttachAsModified(issue, this.ChangeSet.GetOriginal(issue));
            }
            else
                throw new ValidationException(ErrorResources.NoPermissionToUpdateIssue);    
        }

        public void DeleteIssue(Issue issue)
        {
            if ((issue.EntityState == EntityState.Detached))
            {
                this.ObjectContext.Issues.Attach(issue);
            }
            this.ObjectContext.Issues.DeleteObject(issue);
        }

        private class ServerBugCount
        {
            public Int32 Month { get; set; }
            public Int32 Year { get; set; }
            public Int32 Count { get; set; }
        }

        /// <summary>
        /// Return the active bug count for the last numberOfMonth months
        /// </summary>
        /// <param name="numberOfMonth"></param>
        /// <returns></returns>
        [Invoke]
        public string[] GetActiveBugCountByMonth(Int32 numberOfMonth)
        {
            var issueCountsByMonth = from n in this.ObjectContext.Issues
                             where (n.ResolutionID == null || n.ResolutionID == 0)
                             group n by new { month = n.OpenedDate.Month, year = n.OpenedDate.Year } into d
                             select new ServerBugCount { 
                                 Month = d.Key.month, 
                                 Year = d.Key.year, 
                                 Count = d.Count()
                             };

            List<string> resultList = new List<string>();
            // loop through issueCountsByMonth for the last numberOfMonth
            for (int i = numberOfMonth; i >= 1 ; i--)
            {
                DateTime dt = DateTime.Today.AddMonths(-i);
                // search issueCountsByMonth for the issue counts of a specific month
                ServerBugCount currentIssueCount = issueCountsByMonth
                    .Where(n => (n.Month == dt.Month && n.Year == dt.Year))
                    .FirstOrDefault();

                if (currentIssueCount == null)
                {
                    // the active issue count for that month is zero
                    resultList.Add(dt.ToString("MM/yyyy") + "/0");
                }
                else
                {
                    // the active issue count for that month is not zero
                    resultList.Add(currentIssueCount.Month.ToString("00") + "/" + currentIssueCount.Year.ToString() + "/" + currentIssueCount.Count.ToString());
                }
            }
            return resultList.ToArray();
        }

        /// <summary>
        /// Return the resolved bug count for the last numberOfMonth months
        /// </summary>
        /// <param name="numberOfMonth"></param>
        /// <returns></returns>
        [Invoke]
        public string[] GetResolvedBugCountByMonth(Int32 numberOfMonth)
        {
            var issueCountsByMonth = from n in this.ObjectContext.Issues
                                     where (n.ResolutionID != null && n.ResolutionID != 0)
                                     group n by new
                                     { 
                                         month = (n.ResolutionDate ?? DateTime.Now).Month,
                                         year = (n.ResolutionDate ?? DateTime.Now).Year
                                     } into d
                                     select new ServerBugCount
                                     {
                                         Month = d.Key.month,
                                         Year = d.Key.year,
                                         Count = d.Count()
                                     };

            List<string> resultList = new List<string>();
            // loop through issueCountsByMonth for the last numberOfMonth
            for (int i = numberOfMonth; i >= 1; i--)
            {
                DateTime dt = DateTime.Today.AddMonths(-i);
                // search issueCountsByMonth for the issue counts of a specific month
                ServerBugCount currentIssueCount = issueCountsByMonth
                    .Where(n => (n.Month == dt.Month && n.Year == dt.Year))
                    .FirstOrDefault();

                if (currentIssueCount == null)
                {
                    // the resolved issue count for that month is zero
                    resultList.Add(dt.ToString("MM/yyyy") + "/0");
                }
                else
                {
                    // the resolved issue count for that month is not zero
                    resultList.Add(currentIssueCount.Month.ToString("00") + "/" + currentIssueCount.Year.ToString() + "/" + currentIssueCount.Count.ToString());
                }
            }
            return resultList.ToArray();
        }

        private class ServerBugCountByPriority
        {
            public Int32 Priority { get; set; }
            public Int32 Count { get; set; }
        }

        /// <summary>
        /// Return the active bug count by priority
        /// </summary>
        /// <param name="numberOfMonth"></param>
        /// <returns></returns>
        [Invoke]
        public string[] GetActiveBugCountByPriority()
        {
            var issueCountsByPriority = from n in this.ObjectContext.Issues
                                     where (n.ResolutionID == null || n.ResolutionID == 0)
                                     group n by new { priority = n.Priority } into d
                                     select new ServerBugCountByPriority
                                     {
                                         Priority = d.Key.priority,
                                         Count = d.Count()
                                     };

            List<string> resultList = new List<string>();
            // loop through issueCountsByPriority
            for (byte i = IssueVisionServiceConstant.HighestPriority; i <= IssueVisionServiceConstant.LowestPriority; i++)
            {
                // search issueCountsByPriority for the issue counts of a specific priority
                ServerBugCountByPriority currentIssueCount = issueCountsByPriority
                    .Where(n => (n.Priority == i))
                    .FirstOrDefault();

                if (currentIssueCount == null)
                {
                    // the active issue count for that priority is zero
                    resultList.Add(i.ToString() + "/0");
                }
                else
                {
                    // the active issue count for that priority is not zero
                    resultList.Add(currentIssueCount.Priority.ToString() + "/" + currentIssueCount.Count.ToString());
                }
            }
            return resultList.ToArray();
        }

        public IQueryable<IssueHistory> GetIssueHistories()
        {
            return this.ObjectContext.IssueHistories;
        }

        public IQueryable<IssueType> GetIssueTypes()
        {
            return this.ObjectContext.IssueTypes;
        }

        public IQueryable<Platform> GetPlatforms()
        {
            return this.ObjectContext.Platforms;
        }

        public IQueryable<Resolution> GetResolutions()
        {
            return this.ObjectContext.Resolutions;
        }

        public IQueryable<SecurityQuestion> GetSecurityQuestions()
        {
            return this.ObjectContext.SecurityQuestions;
        }

        public IQueryable<Status> GetStatuses()
        {
            return this.ObjectContext.Statuses;
        }

        public IQueryable<SubStatus> GetSubStatuses()
        {
            return this.ObjectContext.SubStatuses;
        }

        public IQueryable<User> GetUsers()
        {
            return this.ObjectContext.Users;
        }

        /// <summary>
        /// Get the user information for the currently login user
        /// </summary>
        /// <returns></returns>
        [Query(IsComposable = false)]
        public User GetCurrentUser()
        {
            return this.ObjectContext.Users.FirstOrDefault(u => u.Name == this.ServiceContext.User.Identity.Name);
        }

        public void InsertUser(User user)
        {
            // check for insert user permission
            if (CheckUserInsertPermission(user) && user.IsUserMaintenance)
            {
                // Re-generate password hash and password salt
                user.PasswordSalt = HashHelper.CreateRandomSalt();
                user.PasswordHash = HashHelper.ComputeSaltedHash(user.NewPassword, user.PasswordSalt);

                // set a valid PasswordQuestion
                SecurityQuestion securityQuestion = this.ObjectContext.SecurityQuestions.FirstOrDefault();
                if (securityQuestion != null)
                    user.PasswordQuestion = securityQuestion.PasswordQuestion;
                // set PasswordAnswer that no body knows
                user.PasswordAnswerSalt = HashHelper.CreateRandomSalt();
                user.PasswordAnswerHash = HashHelper.CreateRandomSalt();

                // requires the user to reset profile
                user.ProfileReset = (byte)1;

                if ((user.EntityState != EntityState.Detached))
                {
                    this.ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Added);
                }
                else
                {
                    this.ObjectContext.Users.AddObject(user);
                }
            }
            else
                throw new ValidationException(ErrorResources.NoPermissionToInsertUser);  
        }

        public void UpdateUser(User currentUser)
        {
            if (currentUser.IsUserMaintenance)
            {
                // the call is from UserMaintenance screen
                if (currentUser.Name == this.ServiceContext.User.Identity.Name)
                {   // the caller is updateing itself
                    // Search user from database by name
                    User foundUser = this.ObjectContext.Users.FirstOrDefault(u => u.Name == currentUser.Name);
                    if (foundUser != null)
                    {
                        // verify whether the caller is admin
                        if (IsAdminUser(foundUser))
                        {
                            // Re-generate password hash and password salt
                            foundUser.PasswordSalt = HashHelper.CreateRandomSalt();
                            foundUser.PasswordHash = HashHelper.ComputeSaltedHash(currentUser.NewPassword, foundUser.PasswordSalt);

                            foundUser.FirstName = currentUser.FirstName;
                            foundUser.LastName = currentUser.LastName;
                            foundUser.Email = currentUser.Email;
                            foundUser.UserType = currentUser.UserType;
                            // requires the foundUser to reset profile
                            foundUser.ProfileReset = (byte)1;
                        }
                        else
                            throw new ValidationException(ErrorResources.NoPermissionToUpdateUser);
                    }
                    else
                        throw new ValidationException(ErrorResources.NoPermissionToUpdateUser);
                }
                else
                {   // the caller is updating someone else
                    // verify whether the caller is admin
                    if (IsAdminUser(GetUserByName(this.ServiceContext.User.Identity.Name)))
                    {
                        // Re-generate password hash and password salt
                        currentUser.PasswordSalt = HashHelper.CreateRandomSalt();
                        currentUser.PasswordHash = HashHelper.ComputeSaltedHash(currentUser.NewPassword, currentUser.PasswordSalt);

                        // requires the currentUser to reset profile
                        currentUser.ProfileReset = (byte)1;

                        this.ObjectContext.Users.AttachAsModified(currentUser, this.ChangeSet.GetOriginal(currentUser));
                    }
                    else
                        throw new ValidationException(ErrorResources.NoPermissionToUpdateUser);
                }
            }
            else
            {
                // the call is from MyProfile screen
                // and the caller can only update themselves
                if (currentUser.Name == this.ServiceContext.User.Identity.Name)
                {
                    // Search user from database by name
                    User foundUser = this.ObjectContext.Users.FirstOrDefault(u => u.Name == currentUser.Name);

                    if (foundUser != null)
                    {
                        // generate password hash
                        string passwordHash = HashHelper.ComputeSaltedHash(currentUser.Password, foundUser.PasswordSalt);

                        if (string.Equals(passwordHash, foundUser.PasswordHash, StringComparison.Ordinal))
                        {
                            // Re-generate password hash and password salt
                            foundUser.PasswordSalt = HashHelper.CreateRandomSalt();
                            foundUser.PasswordHash = HashHelper.ComputeSaltedHash(currentUser.NewPassword, foundUser.PasswordSalt);

                            // set the new password question
                            foundUser.PasswordQuestion = currentUser.PasswordQuestion;

                            // re-generate passwordAnswer hash and passwordAnswer salt
                            foundUser.PasswordAnswerSalt = HashHelper.CreateRandomSalt();
                            foundUser.PasswordAnswerHash = HashHelper.ComputeSaltedHash(currentUser.PasswordAnswer, foundUser.PasswordAnswerSalt);

                            foundUser.FirstName = currentUser.FirstName;
                            foundUser.LastName = currentUser.LastName;
                            foundUser.Email = currentUser.Email;
                            // no need to reset profile for the foundUser
                            foundUser.ProfileReset = (byte)0;
                        }
                        else
                            throw new UnauthorizedAccessException(ErrorResources.PasswordDoesNotMatch);
                    }
                    else
                        throw new ValidationException(ErrorResources.NoPermissionToUpdateUser);
                }
                else
                    throw new ValidationException(ErrorResources.NoPermissionToUpdateUser);
            }
        }

        public void DeleteUser(User user)
        {
            // check for delete user permission
            if (CheckUserDeletePermission(user))
            {
                if ((user.EntityState == EntityState.Detached))
                {
                    this.ObjectContext.Users.Attach(user);
                }
                this.ObjectContext.Users.DeleteObject(user);
            }
            else
                throw new ValidationException(ErrorResources.NoPermissionToDeleteUser); 
        }

        #region "Private Methods"

        private User GetUserByName(string userName)
        {
            return this.ObjectContext.Users.FirstOrDefault(u => u.Name == userName);
        }

        private bool IsAdminUser(User user)
        {
            return (user.UserType == "A");
        }

        private bool IssueIsReadOnly(Issue currentIssue)
        {
            // Admin user can read/update any issue
            if (IsAdminUser(GetUserByName(this.ServiceContext.User.Identity.Name)))
                return false;

            // normal user can only read/update issues assigned to them
            // or issues created by them and have not assigned to anyone.
            if (currentIssue.AssignedToID != null
                && currentIssue.AssignedToID == this.ServiceContext.User.Identity.Name)
                return false;
            else if (currentIssue.AssignedToID == null
                && currentIssue.OpenedByID == this.ServiceContext.User.Identity.Name)
                return false;

            return true;
        }

        private bool CheckUserInsertPermission(User user)
        {
            // cannot add itself
            if (user.Name == this.ServiceContext.User.Identity.Name)
                return false;

            // only admin user can insert a new user
            if (IsAdminUser(GetUserByName(this.ServiceContext.User.Identity.Name)))
                return true;
            else
                return false;
        }

        private bool CheckUserDeletePermission(User user)
        {
            // cannot delete itself
            if (user.Name == this.ServiceContext.User.Identity.Name)
                return false;

            // only admin user can delete a user
            if (IsAdminUser(GetUserByName(this.ServiceContext.User.Identity.Name)))
                return true;
            else
                return false;
        }

        #endregion "Private Methods"
    }
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Weidong Shen
Software Developer (Senior)
United States United States
Weidong has been an information system professional since 1990. He has a Master's degree in Computer Science, and is currently a MCSD .NET

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 7 Jul 2011
Article Copyright 2010 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid