Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / XAML

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

Rate me:
Please Sign up or sign in to vote.
4.84/5 (108 votes)
7 Jul 2011CPOL9 min read 2.1M   30.9K   298  
Part 1 of a series describing the creation of a Silverlight business application using MEF, MVVM Light, and WCF RIA Services.

namespace IssueVision.Data.Web
{
    using System;
    using System.Collections.Generic;
    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 ObjectContext.Attributes;
        }

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

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

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

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

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

        public void InsertFile(File file)
        {
            if (file.FileID == Guid.Empty)
                file.FileID = Guid.NewGuid();

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

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

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

        /// <summary>
        /// Get all issues orderby by StatusID, and Priority
        /// </summary>
        /// <returns></returns>
        public IQueryable<Issue> GetIssues()
        {
            return 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 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 ObjectContext.Issues
                .Include("Files").Include("Attributes")
                .Where(g => g.AssignedToID.Equals(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 = ServiceContext.User.Identity.Name;
            issue.LastChange = DateTime.Now;
            issue.ChangedByID = 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
            {
                if (issue.ResolutionDate == null)
                    issue.ResolutionDate = DateTime.Now;
                if (issue.ResolvedByID == null)
                    issue.ResolvedByID = ServiceContext.User.Identity.Name;
            }

            if ((issue.EntityState != EntityState.Detached))
            {
                ObjectContext.ObjectStateManager.ChangeObjectState(issue, EntityState.Added);
            }
            else
            {
                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(ChangeSet.GetOriginal(issue)))
            {
                issue.LastChange = DateTime.Now;
                issue.ChangedByID = 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
                {
                    if (issue.ResolutionDate == null)
                        issue.ResolutionDate = DateTime.Now;
                    if (issue.ResolvedByID == null)
                        issue.ResolvedByID = ServiceContext.User.Identity.Name;
                }

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

        public void DeleteIssue(Issue issue)
        {
            if ((issue.EntityState == EntityState.Detached))
            {
                ObjectContext.Issues.Attach(issue);
            }
            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 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()
                                     };

            var resultList = new List<string>();
            // loop through issueCountsByMonth for the last numberOfMonth
            for (var i = numberOfMonth; i >= 1; i--)
            {
                var 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 + "/" + currentIssueCount.Count);
                }
            }
            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 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()
                                     };

            var resultList = new List<string>();
            // loop through issueCountsByMonth for the last numberOfMonth
            for (var i = numberOfMonth; i >= 1; i--)
            {
                var 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 + "/" + currentIssueCount.Count);
                }
            }
            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>
        /// <returns></returns>
        [Invoke]
        public string[] GetActiveBugCountByPriority()
        {
            var issueCountsByPriority = from n in 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()
                                        };

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

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

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

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

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

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

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

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

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

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

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

        public void InsertUser(User user)
        {
            // check for insert user permission
            if (CheckUserInsertPermission(user) && user.IsUserMaintenance)
            {
                // validate whether the user already exists
                User foundUser = ObjectContext.Users.Where(
                    n => n.Name == user.Name).FirstOrDefault();
                if (foundUser != null)
                    throw new ValidationException(ErrorResources.CannotInsertDuplicateUser);

                // 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 = 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 = 1;

                if ((user.EntityState != EntityState.Detached))
                {
                    ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Added);
                }
                else
                {
                    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 == ServiceContext.User.Identity.Name)
                {   // the caller is updateing itself
                    // Search user from database by name
                    User foundUser = 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 = 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(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 = 1;

                        ObjectContext.Users.AttachAsModified(currentUser, 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 == ServiceContext.User.Identity.Name)
                {
                    // Search user from database by name
                    User foundUser = 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 = 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))
            {
                // validate the FOREIGN KEY contraint [FK_Issues_AssignedTo] from table [Issues]
                Issue foundIssue = ObjectContext.Issues.Where(
                    n => n.AssignedToID == user.Name).FirstOrDefault();
                if (foundIssue != null)
                    throw new ValidationException(ErrorResources.CannotDeleteUserAssignedToID);

                // validate the FOREIGN KEY contraint [FK_Issues_ChangedBy] from table [Issues]
                foundIssue = ObjectContext.Issues.Where(
                    n => n.ChangedByID == user.Name).FirstOrDefault();
                if (foundIssue != null)
                    throw new ValidationException(ErrorResources.CannotDeleteUserChangedByID);

                // validate the FOREIGN KEY contraint [FK_Issues_OpenedBy] from table [Issues]
                foundIssue = ObjectContext.Issues.Where(
                    n => n.OpenedByID == user.Name).FirstOrDefault();
                if (foundIssue != null)
                    throw new ValidationException(ErrorResources.CannotDeleteUserOpenedByID);

                // validate the FOREIGN KEY contraint [FK_Issues_ResolvedBy] from table [Issues]
                foundIssue = ObjectContext.Issues.Where(
                    n => n.ResolvedByID == user.Name).FirstOrDefault();
                if (foundIssue != null)
                    throw new ValidationException(ErrorResources.CannotDeleteUserResolvedByID);

                if ((user.EntityState == EntityState.Detached))
                {
                    ObjectContext.Users.Attach(user);
                }
                ObjectContext.Users.DeleteObject(user);
            }
            else
                throw new ValidationException(ErrorResources.NoPermissionToDeleteUser);
        }

        #region "Private Methods"

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

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

        private bool IssueIsReadOnly(Issue currentIssue)
        {
            // Admin user can read/update any issue
            if (IsAdminUser(GetUserByName(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 == ServiceContext.User.Identity.Name)
                return false;
            if (currentIssue.AssignedToID == null
                && currentIssue.OpenedByID == ServiceContext.User.Identity.Name)
                return false;

            return true;
        }

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

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

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

            // only admin user can delete a user
            if (IsAdminUser(GetUserByName(ServiceContext.User.Identity.Name)))
                return true;
            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)


Written By
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

Comments and Discussions