diff --git a/DataManager/ModelMapperProfile.cs b/DataManager/ModelMapperProfile.cs index 9b80273..0b1a521 100644 --- a/DataManager/ModelMapperProfile.cs +++ b/DataManager/ModelMapperProfile.cs @@ -129,6 +129,7 @@ public ModelMapperProfile(IModelCache modelCache) // Mapping incident data CreateMap() .ConstructUsing(source => modelCache.PutOrGetModel(new IncidentReviewModel(source.ReviewId))) + .ForMember(dest => dest.InvolvedMembers, opt => opt.MapFrom(src => src.InvolvedMemberIds.Select(x => GetLeagueMember(x, modelCache)))) .ForMember(dest => dest.InvolvedMembers, opt => opt.UseDestinationValue()) .ForMember(dest => dest.Comments, opt => opt.UseDestinationValue()) .ForMember(dest => dest.AcceptedReviewVotes, opt => opt.UseDestinationValue()) @@ -139,6 +140,7 @@ public ModelMapperProfile(IModelCache modelCache) dest.InitReset(); }) .ReverseMap() + .ForMember(dest => dest.InvolvedMemberIds, opt => opt.MapFrom(src => src.InvolvedMembers.Select(x => x.MemberId))) .IncludeBase(); CreateMap() //.ConstructUsing(source => new IncidentReviewInfo() { ReviewId = source.ReviewId }) @@ -256,12 +258,11 @@ public ModelMapperProfile(IModelCache modelCache) CurrentSessions = new SessionModel[0]; } }) - //.ForMember(dest => dest.Season, opt => opt.Ignore()) - //.ForMember(dest => dest.Schedule, opt => opt.Ignore()) - //.ForMember(dest => dest.Location, opt => opt.MapFrom(src => src.LocationId)) .EqualityComparison((src, dest) => src.SessionId == dest.SessionId) //.ConstructUsing(source => (source.SessionType == SessionType.Race) ? new RaceSessionModel(source.SessionId) : new SessionModel(source.SessionId, source.SessionType)) .ConstructUsing(source => modelCache.PutOrGetModel(new SessionModel(source.SessionId, source.SessionType))) + .ForMember(dest => dest.SessionResult, opt => opt.MapFrom(src => src.SessionResultId != null ? new ResultInfo(src.SessionResultId) : null)) + .ForMember(dest => dest.Reviews, opt => opt.MapFrom(src => src.ReviewIds.Select(x => new IncidentReviewInfo() { ReviewId = x }))) .ForMember(dest => dest.Reviews, opt => opt.UseDestinationValue()) .AfterMap((src, dest) => { @@ -289,6 +290,8 @@ public ModelMapperProfile(IModelCache modelCache) }); CreateMap() //.ForMember(dest => dest.LocationId, opt => opt.MapFrom(source => source.Location)) + .ForMember(dest => dest.SessionResultId, opt => opt.MapFrom(src => src.SessionResult != null ? src.SessionResult.ResultId : null)) + .ForMember(dest => dest.ReviewIds, opt => opt.MapFrom(src => src.Reviews.Select(x => x.ReviewId.GetValueOrDefault()))) .Include(); CreateMap(); //.ForMember(dest => dest.LocationId, opt => opt.MapFrom(source => source.Location)); @@ -307,9 +310,13 @@ public ModelMapperProfile(IModelCache modelCache) CreateMap() .ConstructUsing(source => modelCache.PutOrGetModel(new ResultModel(source.ResultId.GetValueOrDefault()))) .EqualityComparison((src, dest) => src.ResultId == dest.ResultId) + .ForMember(dest => dest.Session, opt => opt.MapFrom(src => new SessionInfo(src.SessionId, SessionType.Undefined))) .ForMember(dest => dest.RawResults, opt => opt.UseDestinationValue()) + .ForMember(dest => dest.Reviews, opt => opt.MapFrom(src => src.ReviewIds.Select(x => new IncidentReviewInfo() { ReviewId = x }))) .ForMember(dest => dest.Reviews, opt => opt.UseDestinationValue()) - .ReverseMap(); + .ReverseMap() + .ForMember(dest => dest.SessionId, opt => opt.MapFrom(src => src.Session.SessionId)) + .ForMember(dest => dest.ReviewIds, opt => opt.MapFrom(src => src.Reviews.Select(x => x.ReviewId.GetValueOrDefault()))); CreateMap() .ReverseMap(); @@ -321,14 +328,19 @@ public ModelMapperProfile(IModelCache modelCache) CreateMap() .ConstructUsing(source => modelCache.PutOrGetModel(new ResultRowModel(source.ResultRowId))) + .ForMember(dest => dest.Member, opt => opt.MapFrom(src => GetLeagueMember(src.MemberId, modelCache))) .ForMember(dest => dest.Location, opt => opt.MapFrom((src, trg) => LeagueContext.Locations.FirstOrDefault(x => x.LocationId == src.LocationId))) .EqualityComparison((src, dest) => src.ResultRowId == dest.ResultRowId) - - .ReverseMap(); + .ReverseMap() + .ForMember(dest => dest.MemberId, opt => opt.MapFrom(src => src.Member.MemberId)) + .ForMember(dest => dest.LocationId, opt => opt.MapFrom(src => src.Location != null ? src.Location.LocationId : null)); CreateMap() .ConstructUsing(source => modelCache.PutOrGetModel(new ScoringModel(source.ScoringId))) .EqualityComparison((src, dest) => src.ScoringId == dest.ScoringId) + .ForMember(dest => dest.ConnectedSchedule, opt => opt.MapFrom(src => src.ConnectedScheduleId != null ? new ScheduleInfo(src.ConnectedScheduleId) : null)) + .ForMember(dest => dest.Sessions, opt => opt.MapFrom(src => src.SessionIds.Select(x => new SessionInfo(x, SessionType.Undefined)))) + .ForMember(dest => dest.ExtScoringSource, opt => opt.MapFrom(src => src.ExtScoringSourceId != null ? new ScoringInfo(src.ExtScoringSourceId) : null)) .ForMember(dest => dest.BasePoints, opt => opt.MapFrom((src, dest, result) => { ObservableCollection pairs = new ObservableCollection(); @@ -365,6 +377,9 @@ public ModelMapperProfile(IModelCache modelCache) .ForMember(dest => dest.ResultsFilterOptionIds, opt => opt.UseDestinationValue()) .ForMember(dest => dest.IncPenaltyPoints, opt => opt.Ignore()) .ReverseMap() + .ForMember(dest => dest.ConnectedScheduleId, opt => opt.MapFrom(src => src.ConnectedSchedule != null ? src.ConnectedSchedule.ScheduleId : null)) + .ForMember(dest => dest.SessionIds, opt => opt.MapFrom(src => src.Sessions.Select(x => x.SessionId))) + .ForMember(dest => dest.ExtScoringSourceId, opt => opt.MapFrom(src => src.ExtScoringSource != null ? src.ExtScoringSource.ScoringId : null)) .ForMember(dest => dest.BasePoints, opt => opt.MapFrom(src => (src.BasePoints.Count > 0) ? src.BasePoints.Select(x => x.Value.ToString()).Aggregate((x, y) => x + " " + y) : "")) .ForMember(dest => dest.BonusPoints, opt => opt.MapFrom(src => (src.BonusPoints.Count > 0) ? src.BonusPoints.Select(x => x.Key + ":" + x.Value.ToString()).Aggregate((x, y) => x + " " + y) : "")) .ForMember(dest => dest.IncPenaltyPoints, opt => opt.Ignore()); @@ -380,6 +395,7 @@ public ModelMapperProfile(IModelCache modelCache) CreateMap() .ConstructUsing(source => modelCache.PutOrGetModel(new ScoringTableModel() { ScoringTableId = source.ScoringTableId})) .EqualityComparison((src, dest) => src.ScoringTableId == dest.ScoringTableId) + .ForMember(dest => dest.Sessions, opt => opt.MapFrom(src => src.SessionIds.Select(x => new SessionInfo(x, SessionType.Undefined)))) .ForMember(dest => dest.Scorings, opt => opt.MapFrom((src, dest, result, context) => { List factors = new List(); @@ -403,26 +419,28 @@ public ModelMapperProfile(IModelCache modelCache) if (factors.Count() == 0 || success == false) { - factors = src.Scorings.Select(x => (double)1).ToList(); + factors = src.ScoringIds.Select(x => (double)1).ToList(); } var mapper = context.Mapper; - var destMultiScorings = src.Scorings.Select((x, i) => new MyKeyValuePair(mapper.Map(x), factors.ElementAt(i))); + //var destMultiScorings = src.Scorings.Select((x, i) => new MyKeyValuePair(mapper.Map(x), factors.ElementAt(i))); + var destMultiScorings = src.ScoringIds.Select((x, i) => new MyKeyValuePair(mapper.Map(modelCache.PutOrGetModel(new ScoringInfo(x))), factors.ElementAt(i))); return new ObservableCollection>(destMultiScorings); })) .ForMember(dest => dest.Scorings, opt => opt.UseDestinationValue()) .ReverseMap() + .ForMember(dest => dest.SessionIds, opt => opt.MapFrom(src => src.Sessions.Select(x => x.SessionId))) .ForMember(dest => dest.ScoringFactors, opt => opt.MapFrom((src, dest, factors) => { if (src.Scorings?.Count > 0) return src.Scorings.Select(x => x.Value.ToString()).Aggregate((x, y) => x + ";" + y); return null; })) - .ForMember(dest => dest.Scorings, opt => opt.MapFrom((src, dest, scorings) => + .ForMember(dest => dest.ScoringIds, opt => opt.MapFrom((src, dest, scoringIds) => { if (src.Scorings?.Count > 0) - return src.Scorings.Select(x => x.Key).ToArray(); - return new ScoringInfo[0]; + return src.Scorings.Select(x => x.Key.ScoringId.GetValueOrDefault()).Where(x => x != default).ToArray(); + return new long[0]; })); CreateMap() //.ConstructUsing(source => ModelCache.PutOrGetModel(new ScoredResultRowModel() { ScoredResultRowId = source.ScoredResultRowId})) @@ -430,20 +448,18 @@ public ModelMapperProfile(IModelCache modelCache) .ForMember(dest => dest.Location, opt => opt.MapFrom((src, trg) => LeagueContext.Locations.FirstOrDefault(x => x.LocationId == src.LocationId))) .EqualityComparison((src, dest) => src.ScoredResultRowId == dest.ScoredResultRowId) .IncludeBase(); - //.EqualityComparison((src, dest) => src.ScoredResultRowId == dest.ScoredResultRowId) + //.EqualityComparison((src, dest) => src.ScoredResultRowId == dest.ScoredResultRowId) CreateMap() - .ConstructUsing(source => modelCache.PutOrGetModel(new ScoredResultModel() { Scoring = new ScoringInfo(source.Scoring.ScoringId), ResultId = source.ResultId})) - .EqualityComparison((src, dest) => src.Session.SessionId == dest.Session.SessionId && src.Scoring.ScoringId == dest.Scoring.ScoringId) + .ConstructUsing(source => modelCache.PutOrGetModel(new ScoredResultModel() { Scoring = new ScoringInfo(source.ScoringId), ResultId = source.ResultId })) + .EqualityComparison((src, dest) => src.SessionId == dest.Session.SessionId && src.ScoringId == dest.Scoring.ScoringId) + .ForMember(dest => dest.Scoring, opt => opt.MapFrom(src => new ScoringInfo(src.ScoringId))) .ForMember(dest => dest.FinalResults, opt => opt.UseDestinationValue()) - .Include() - //.AfterMap((src, dest) => dest.FinalResults = new ObservableCollection(dest.FinalResults.OrderBy(x => x.FinalPosition))) - //.ForMember(dest => dest.FinalResults, opt => opt.MapFrom(src => src.ScoredResults)) - ; + .Include(); CreateMap() - .ConstructUsing(source => modelCache.PutOrGetModel(new ScoredTeamResultModel() { Scoring = new ScoringInfo(source.Scoring.ScoringId), ResultId = source.ResultId })) - .EqualityComparison((src, dest) => src.Session.SessionId == dest.Session.SessionId && src.Scoring.ScoringId == dest.Scoring.ScoringId) + .ConstructUsing(source => modelCache.PutOrGetModel(new ScoredTeamResultModel() { Scoring = new ScoringInfo(source.ScoringId), ResultId = source.ResultId })) + .EqualityComparison((src, dest) => src.SessionId == dest.Session.SessionId && src.ScoringId == dest.Scoring.ScoringId) .ForMember(dest => dest.FinalResults, opt => opt.UseDestinationValue()) .ForMember(dest => dest.TeamResults, opt => opt.MapFrom((src, trg) => src.TeamResults.OrderBy(x => x.FinalPosition))) .ForMember(dest => dest.TeamResults, opt => opt.UseDestinationValue()) @@ -461,13 +477,19 @@ public ModelMapperProfile(IModelCache modelCache) CreateMap() .ConstructUsing(source => modelCache.PutOrGetModel(new StandingsModel() { ScoringTableId = source.ScoringTableId, SessionId = source.SessionId })) .EqualityComparison((src, dest) => src.ScoringTableId == dest.ScoringTableId) + .ForMember(dest => dest.MostWinsDriver, opt => opt.MapFrom(src => GetLeagueMember(src.MostWinsDriverId, modelCache))) + .ForMember(dest => dest.MostPolesDriver, opt => opt.MapFrom(src => GetLeagueMember(src.MostPolesDriverId, modelCache))) + .ForMember(dest => dest.CleanestDriver, opt => opt.MapFrom(src => GetLeagueMember(src.CleanestDriverId, modelCache))) + .ForMember(dest => dest.MostPenaltiesDriver, opt => opt.MapFrom(src => GetLeagueMember(src.MostPenaltiesDriverId, modelCache))) .ForMember(dest => dest.StandingsRows, opt => opt.UseDestinationValue()) .Include(); + CreateMap() //.ConstructUsing(source => ModelCache.PutOrGetModel(new StandingsRowModel() { Scoring = new ScoringInfo(source.Scoring.ScoringId), Member = new LeagueMember(source.Member.MemberId) })) .ConstructUsing(source => new StandingsRowModel()) + .ForMember(dest => dest.Member, opt => opt.MapFrom(src => GetLeagueMember(src.MemberId, modelCache))) .ForMember(dest => dest.CountedResults, opt => opt.MapFrom(src => src.CountedResults.OrderBy(x => x.Date))) - .EqualityComparison((src, dest) => src.Member.MemberId == dest.Member.MemberId) + .EqualityComparison((src, dest) => src.MemberId == dest.Member.MemberId) .Include(); CreateMap() @@ -499,9 +521,11 @@ public ModelMapperProfile(IModelCache modelCache) CreateMap() .ConstructUsing(src => new ReviewVoteModel() { ReviewVoteId = src.ReviewVoteId }) .EqualityComparison((src, dest) => src.ReviewVoteId == dest.ReviewVoteId) + .ForMember(dest => dest.MemberAtFault, opt => opt.MapFrom(src => GetLeagueMember(src.MemberAtFaultId, modelCache))) .ForMember(dest => dest.VoteCategory, opt => opt .MapFrom(src => src.VoteCategoryId != null ? modelCache.PutOrGetModel(new VoteCategoryModel() { CatId = src.VoteCategoryId.Value }) : null)) .ReverseMap() + .ForMember(dest => dest.MemberAtFaultId, opt => opt.MapFrom(src => src.MemberAtFault != null ? src.MemberAtFault.MemberId : null)) .ForMember(dest => dest.VoteCategoryId, opt => opt.MapFrom(src => src.VoteCategory != null ? src.VoteCategory.CatId : (long?)null)); //.ForMember(dest => dest.AdminId, opt => opt.MapFrom(src => (src.Admin != null) ? (int?)src.Admin.AdminId : null)) @@ -635,9 +659,9 @@ public IncidentReviewInfo Test(IncidentReviewInfoDTO dto) return review; } - public LeagueMember GetLeagueMember(long memberId, IModelCache modelCache) + public LeagueMember GetLeagueMember(long? memberId, IModelCache modelCache) { - return modelCache.PutOrGetModel(new LeagueMember(memberId)); + return memberId != null ? modelCache.PutOrGetModel(new LeagueMember(memberId)) : null; } } diff --git a/DataManager/Models/ModelBase.cs b/DataManager/Models/ModelBase.cs index 86c658e..698cba2 100644 --- a/DataManager/Models/ModelBase.cs +++ b/DataManager/Models/ModelBase.cs @@ -29,6 +29,7 @@ using System.Runtime.CompilerServices; using System.Collections.Specialized; using System.Collections; +using iRLeagueDatabase.Extensions; namespace iRLeagueManager.Models { @@ -61,7 +62,30 @@ public virtual void CopyTo(ModelBase targetObject, params string[] excludeProper if (property.GetMethod == null || property.SetMethod == null) continue; - property.SetValue(targetObject, property.GetValue(this)); + if (property.PropertyType.IsGenericType && + property.PropertyType.GetInterfaces().Any(x => x.GetGenericTypeDefinition() == typeof(ICollection<>))) + { + var interfaceType = property.PropertyType.GetInterfaces().SingleOrDefault(x => x == typeof(ICollection<>)); + // If target type implements ICollection use target collection instead of overwriting property + dynamic targetCollection = property.GetValue(targetObject); + IEnumerable sourceCollection = property.GetValue(this) as IEnumerable; + if (targetCollection == null || sourceCollection == null) + { + property.SetValue(targetObject, sourceCollection); + } + else + { + targetCollection.Clear(); + foreach(var item in sourceCollection) + { + targetCollection.Add(item); + } + } + } + else + { + property.SetValue(targetObject, property.GetValue(this)); + } } if (isInitialized) @@ -85,7 +109,30 @@ public virtual void CopyFrom(ModelBase sourceObject, params string[] excludeProp if (property.GetMethod == null || property.SetMethod == null) continue; - property.SetValue(this, property.GetValue(sourceObject)); + if (property.PropertyType.IsGenericType && + property.PropertyType.GetInterfaces().Any(x => x.GetGenericTypeDefinition() == typeof(ICollection<>))) + { + var interfaceType = property.PropertyType.GetInterfaces().SingleOrDefault(x => x == typeof(ICollection<>)); + // If target type implements ICollection use target collection instead of overwriting property + dynamic targetCollection = property.GetValue(this); + IEnumerable sourceCollection = property.GetValue(sourceObject) as IEnumerable; + if (targetCollection == null || sourceCollection == null) + { + property.SetValue(this, sourceCollection); + } + else + { + targetCollection.Clear(); + foreach (dynamic item in sourceCollection) + { + targetCollection.Add(item); + } + } + } + else + { + property.SetValue(this, property.GetValue(sourceObject)); + } } if (sourceObject.isInitialized) diff --git a/DataManager/Models/Results/ScoredResultRowModel.cs b/DataManager/Models/Results/ScoredResultRowModel.cs index 495a28d..45d642c 100644 --- a/DataManager/Models/Results/ScoredResultRowModel.cs +++ b/DataManager/Models/Results/ScoredResultRowModel.cs @@ -58,72 +58,6 @@ public class ScoredResultRowModel : ResultRowModel public override int PositionChange => StartPosition - FinalPosition; - //public int PositionChange => StartPosition - FinalPosition; - - //public long? ResultRowId { get; internal set; } - //public long ResultId { get; set; } - - ////public override long[] ModelId => new long[] { ResultRowId.GetValueOrDefault(), ResultId }; - - ////private int finalPosition; - ////public int FinalPosition { get => finalPosition; set { finalPosition = value; OnPropertyChanged(); } } - - //private int startPosition; - //public int StartPosition { get => startPosition; set { startPosition = value; OnPropertyChanged(); } } - - //private int finishPosition; - //public int FinishPosition { get => finishPosition; set { finishPosition = value; OnPropertyChanged(); } } - - //private LeagueMember member; - //public LeagueMember Member { get => member; set { member = value; OnPropertyChanged(); OnPropertyChanged(nameof(MemberId)); } } - - //private string teamName; - //public string TeamName { get => teamName; set => SetValue(ref teamName, value); } - - //public long? MemberId { get => Member?.MemberId; } - - //private int carNumber; - //public int CarNumber { get => carNumber; set { carNumber = value; OnPropertyChanged(); } } - - //private int classId; - //public int ClassId { get => classId; set { classId = value; OnPropertyChanged(); } } - - //private string car; - //public string Car { get => car; set { car = value; OnPropertyChanged(); } } - - //private string carClass; - //public string CarClass { get => carClass; set { carClass = value; OnPropertyChanged(); } } - - //public int completedLaps; - //public int CompletedLaps { get => completedLaps; set { completedLaps = value; OnPropertyChanged(); } } - - //private int leadLaps; - //public int LeadLaps { get => leadLaps; set { leadLaps = value; OnPropertyChanged(); } } - - //private int fastLapNr; - //public int FastLapNr { get => fastLapNr; set { fastLapNr = value; OnPropertyChanged(); } } - - //private int incidents; - //public int Incidents { get => incidents; set { incidents = value; OnPropertyChanged(); } } - - //private RaceStatusEnum status; - //public RaceStatusEnum Status { get => status; set { status = value; OnPropertyChanged(); } } - - //private LapTime qualifyingTime = new LapTime(); - //public LapTime QualifyingTime { get => qualifyingTime; set { qualifyingTime = value; OnPropertyChanged(); } } - - //private LapInterval interval; - //public LapInterval Interval { get => interval; set { interval = value; OnPropertyChanged(); } } - - //private LapTime avgLapTime; - //public LapTime AvgLapTime { get => avgLapTime; set { avgLapTime = value; OnPropertyChanged(); } } - - //private LapTime fastestLapTime; - //public LapTime FastestLapTime { get => fastestLapTime; set { fastestLapTime = value; OnPropertyChanged(); } } - - //private Location location; - //public Location Location { get => location; set => SetValue(ref location, value); } - private DateTime date; public DateTime Date { get => date; set => SetValue(ref date, value); } diff --git a/DataManager/Models/Reviews/IncidentReviewModel.cs b/DataManager/Models/Reviews/IncidentReviewModel.cs index 0a08582..1dffc85 100644 --- a/DataManager/Models/Reviews/IncidentReviewModel.cs +++ b/DataManager/Models/Reviews/IncidentReviewModel.cs @@ -36,6 +36,7 @@ using iRLeagueManager.Enums; using System.Collections.ObjectModel; using iRLeagueManager.Models.User; +using iRLeagueDatabase.Extensions; namespace iRLeagueManager.Models.Reviews { @@ -49,8 +50,10 @@ public class IncidentReviewModel : IncidentReviewInfo, IHierarchicalModel //public ScheduleModel Schedule => Session?.Schedule; //public SeasonModel Season => Schedule?.Season; - private SessionInfo session; - public SessionInfo Session { get => session; internal set { SetValue(ref session, value); } } + //private SessionInfo session; + //public SessionInfo Session { get => session; internal set { SetValue(ref session, value); } } + private long sessionId; + public long SessionId { get => sessionId; internal set => SetValue(ref sessionId, value); } private string incidentNr; public string IncidentNr { get => incidentNr; set => SetValue(ref incidentNr, value); } @@ -102,7 +105,7 @@ public IncidentReviewModel(UserModel author, SessionInfo session) : base(author) Comments = new ObservableCollection(); AcceptedReviewVotes = new ObservableCollection(); - Session = session; + SessionId = session.SessionId.GetValueOrDefault(); } //public IncidentReviewModel(ResultModel result) : this () @@ -138,19 +141,19 @@ internal override void InitializeModel() public override void CopyFrom(ModelBase sourceObject, params string[] excludeProperties) { - base.CopyFrom(sourceObject, excludeProperties); + base.CopyFrom(sourceObject, excludeProperties.Concat(new string[] { nameof(Comments), nameof(AcceptedReviewVotes) }).ToArray()); if (sourceObject is IncidentReviewModel reviewModel) { InitReset(); - InvolvedMembers = new ObservableCollection(reviewModel.InvolvedMembers.ToList()); - Comments = new ObservableCollection(reviewModel.Comments.Select(x => - { - var comment = new ReviewCommentModel(); - comment.CopyFrom(x); - return comment; - }).ToList()); - AcceptedReviewVotes = new ObservableCollection(reviewModel.AcceptedReviewVotes.Select(x => + //InvolvedMembkers.MapCollection(reviewModel.InvolvedMembers.ToList()); + Comments.MapCollection(reviewModel.Comments.Select(x => + { + var comment = new ReviewCommentModel(); + comment.CopyFrom(x); + return comment; + }).ToList()); + AcceptedReviewVotes.MapCollection(reviewModel.AcceptedReviewVotes.Select(x => { var vote = new ReviewVoteModel(); vote.CopyFrom(x); @@ -166,19 +169,19 @@ public override void CopyFrom(ModelBase sourceObject, params string[] excludePro public override void CopyTo(ModelBase targetObject, params string[] excludeProperties) { - base.CopyTo(targetObject, excludeProperties); + base.CopyTo(targetObject, excludeProperties.Concat(new string[] { nameof(Comments), nameof(AcceptedReviewVotes) }).ToArray()); if (targetObject is IncidentReviewModel reviewModel) { reviewModel.InitReset(); - reviewModel.InvolvedMembers = new ObservableCollection(InvolvedMembers.ToList()); - reviewModel.Comments = new ObservableCollection(Comments.Select(x => + //reviewModel.InvolvedMembers = new ObservableCollection(InvolvedMembers.ToList()); + reviewModel.Comments.MapCollection(Comments.Select(x => { var comment = new ReviewCommentModel(); comment.CopyFrom(x); return comment; }).ToList()); - reviewModel.AcceptedReviewVotes = new ObservableCollection(AcceptedReviewVotes.Select(x => + reviewModel.AcceptedReviewVotes.MapCollection(AcceptedReviewVotes.Select(x => { var vote = new ReviewVoteModel(); vote.CopyFrom(x); diff --git a/DataManager/Models/Reviews/ReviewCommentModel.cs b/DataManager/Models/Reviews/ReviewCommentModel.cs index 6b957e7..8c540b3 100644 --- a/DataManager/Models/Reviews/ReviewCommentModel.cs +++ b/DataManager/Models/Reviews/ReviewCommentModel.cs @@ -35,6 +35,7 @@ using iRLeagueManager.Interfaces; using System.Collections.ObjectModel; using iRLeagueManager.Models.User; +using iRLeagueDatabase.Extensions; namespace iRLeagueManager.Models.Reviews { @@ -47,8 +48,10 @@ public class ReviewCommentModel : CommentModel, IReviewComment, INotifyPropertyC //public ScheduleModel Schedule => Review?.Schedule; //public override SeasonModel Season => Schedule?.Season; - private IncidentReviewInfo review; - public IncidentReviewInfo Review { get => review; internal set => SetValue(ref review, value); } + //private IncidentReviewInfo review; + //public IncidentReviewInfo Review { get => review; internal set => SetValue(ref review, value); } + private long? reviewId; + public long? ReviewId { get => reviewId; internal set => SetValue(ref reviewId, value); } private ObservableCollection commentReviewVotes; public ObservableCollection CommentReviewVotes { get => commentReviewVotes; set => SetNotifyCollection(ref commentReviewVotes, value); } @@ -70,7 +73,7 @@ public ReviewCommentModel(UserModel author) : base(author) public ReviewCommentModel(UserModel author, IncidentReviewInfo review) : this(author) { - Review = review; + ReviewId = review.ReviewId; } internal override void InitializeModel() @@ -84,13 +87,13 @@ internal override void InitializeModel() public override void CopyFrom(ModelBase sourceObject, params string[] excludeProperties) { - base.CopyFrom(sourceObject, excludeProperties); + base.CopyFrom(sourceObject, excludeProperties.Concat(new string[] { nameof(CommentReviewVotes) }).ToArray()); if (sourceObject is ReviewCommentModel commentModel) { InitReset(); - CommentReviewVotes = new ObservableCollection(commentModel.CommentReviewVotes.Select(x => + CommentReviewVotes.MapCollection(commentModel.CommentReviewVotes.Select(x => { var vote = new ReviewVoteModel(); vote.CopyFrom(x); @@ -106,12 +109,12 @@ public override void CopyFrom(ModelBase sourceObject, params string[] excludePro public override void CopyTo(ModelBase targetObject, params string[] excludeProperties) { - base.CopyTo(targetObject, excludeProperties); + base.CopyTo(targetObject, excludeProperties.Concat(new string[] { nameof(CommentReviewVotes) }).ToArray()); if (targetObject is ReviewCommentModel commentModel) { commentModel.InitReset(); - commentModel.CommentReviewVotes = new ObservableCollection(CommentReviewVotes.Select(x => + commentModel.CommentReviewVotes.MapCollection(CommentReviewVotes.Select(x => { var vote = new ReviewVoteModel(); vote.CopyFrom(x); diff --git a/DataManager/Models/SeasonModel.cs b/DataManager/Models/SeasonModel.cs index 42e5adc..0b59e45 100644 --- a/DataManager/Models/SeasonModel.cs +++ b/DataManager/Models/SeasonModel.cs @@ -38,7 +38,7 @@ namespace iRLeagueManager.Models { - public class SeasonModel : SeasonInfo, IHierarchicalModel //, ISeason + public class SeasonModel : SeasonInfo //, ISeason { private ObservableCollection schedules; public ObservableCollection Schedules @@ -72,8 +72,6 @@ public ScoringModel MainScoring //private ObservableCollection results; //public ObservableCollection Results { get => results; internal set => SetNotifyCollection(ref results, value); } - string IHierarchicalModel.Description => SeasonName; - private DateTime seasonStart; public DateTime SeasonStart { get => seasonStart; internal set => SetValue(ref seasonStart, value); } @@ -89,12 +87,6 @@ public ScoringModel MainScoring private bool hideCommentsBeforeVoted; public bool HideCommentsBeforeVoted { get => hideCommentsBeforeVoted; set => SetValue(ref hideCommentsBeforeVoted, value); } - IEnumerable IHierarchicalModel.Children => new List> { Schedules.Cast() }; - - public IEnumerable GetResults() - { - return null; - } // client.Results.Where(x => Sessions.Select(y => y.SessionId).Contains(x.SessionId)); //public IEnumerable Sessions => Schedules.Select(x => x.Sessions.AsEnumerable()).Aggregate((x, y) => x.Concat(y)); @@ -119,41 +111,6 @@ public SeasonModel(long? seasonId) : this() SeasonId = seasonId; } - //public IEnumerable GetSessions() - //{ - // return Schedules.Select(x => x.Sessions.AsEnumerable()).Aggregate((x, y) => x.Concat(y)); - //} - - //public int GetSessionCount() - //{ - // return GetSessions().Count(); - //} - - //public SessionInfo GetLastSession() - //{ - // return GetSessions().OrderBy(x => x.Date).Last(); - //} - - //internal override void InitReset() - //{ - // foreach (var schedule in Schedules) - // { - // schedule.Season = this; - // schedule.InitReset(); - // } - - // foreach (var result in Results) - // { - // result.InitReset(); - // } - - // foreach (var review in Reviews) - // { - // review.InitReset(); - // } - // base.InitReset(); - //} - internal override void InitializeModel() { if (!isInitialized) @@ -170,26 +127,8 @@ internal override void InitializeModel() } foreach (var scoringTable in ScoringTables) { - //for (int i = 0; i < scoringTable.Scorings.Count(); i++) - //{ - // var scoring = Scorings.SingleOrDefault(x => x.ScoringId == scoringTable.Scorings.ElementAt(i).Key.ScoringId); - // if (scoring != null) - // { - // scoringTable.Scorings.ElementAt(i).Key = scoring; - // } - //} scoringTable.InitializeModel(); } - - //foreach (var result in Results) - //{ - // result.InitializeModel(); - //} - - //foreach (var review in Reviews) - //{ - // review.InitializeModel(); - //} } base.InitializeModel(); } diff --git a/DataManager/Models/Sessions/SessionModel.cs b/DataManager/Models/Sessions/SessionModel.cs index 7df598c..d72cb68 100644 --- a/DataManager/Models/Sessions/SessionModel.cs +++ b/DataManager/Models/Sessions/SessionModel.cs @@ -42,7 +42,7 @@ namespace iRLeagueManager.Models.Sessions /// Base type for league sessions. /// [Serializable()] - public class SessionModel : SessionInfo, IHierarchicalModel, ISession //IRaceSession + public class SessionModel : SessionInfo, ISession //IRaceSession { //private ScheduleModel schedule; //public ScheduleModel Schedule { get => schedule; internal set => SetValue(ref schedule, value); } @@ -74,84 +74,6 @@ public ResultInfo SessionResult [XmlIgnore] public TimeSpan Duration { get => duration; set => SetValue(ref duration, value); } - //string IHierarchicalModel.Description => Date.ToShortDateString() + " - " + Location?.ShortName; - - IEnumerable IHierarchicalModel.Children => (SessionResult != null) ? new object[] { SessionResult } : new object[0]; - - //private int raceId; - ///// - ///// Unique race id for the league - ///// - //public int RaceId { get => raceId; set { raceId, value); } - - //private int laps; - ///// - ///// Number of laps for the race. Set to 0 for time based races. - ///// - //public int Laps { get => laps; set { laps, value); } - - ///// - ///// Xml data of TimeSpan element - ///// - //private TimeSpan practiceLength; - ///// - ///// Length of the free practice. Set to 0:00:00 for no practice or warmup. - ///// - //public TimeSpan PracticeLength { get => practiceLength; set { practiceLength, value); } - - ///// - ///// Xml data of TimeSpan element - ///// - //private TimeSpan qualyLength; - ///// - ///// Length of the attached qualifying. Set to 0:00:00 for no attached qualy. - ///// - //public TimeSpan QualyLength { get => qualyLength; set { qualyLength, value); } - - ///// - ///// Xml data of TimeSpan element - ///// - //private TimeSpan raceLength; - ///// - ///// Length of the race. If length is not time limited - set to 0:00:00 - ///// - //public TimeSpan RaceLength { get => raceLength; set { raceLength, value); } - - //private string irSessionId; - ///// - ///// Session id from iracing.com service - ///// - //public string IrSessionId { get => irSessionId; set { irSessionId, value); } - - //private string irResultLink; - ///// - ///// Link to the iracing.com results page. - ///// - //public string IrResultLink { get => irResultLink; set { irResultLink, value); } - - //private bool qualyAttached; - ///// - ///// Check if session has attached qualifying - ///// - //public bool QualyAttached { get => qualyAttached; set { qualyAttached, value); } - - //private bool practiceAttached; - ///// - ///// Check if session has attached free-practice or warmup - ///// - //public bool PracticeAttached { get => practiceAttached; set { practiceAttached, value); } - - - //public XmlTimeSpan Duration { get; set; } - - /// - /// Create a new Session object - /// - //public SessionBase() - //{ - // SessionType = SessionType.Undefined; - //} - public SessionModel() : this(0, SessionType.Undefined) { } diff --git a/iRLeagueManager/Properties/AssemblyInfo.cs b/iRLeagueManager/Properties/AssemblyInfo.cs index 624f6c6..4e22c9a 100644 --- a/iRLeagueManager/Properties/AssemblyInfo.cs +++ b/iRLeagueManager/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.8.4.0")] -[assembly: AssemblyFileVersion("0.8.4.0")] +[assembly: AssemblyVersion("0.9.0.0")] +[assembly: AssemblyFileVersion("0.9.0.0")] diff --git a/iRLeagueManager/ViewModels/Collections/ObservableViewModelCollection.cs b/iRLeagueManager/ViewModels/Collections/ObservableViewModelCollection.cs index 4d15b4f..4a7ec5e 100644 --- a/iRLeagueManager/ViewModels/Collections/ObservableViewModelCollection.cs +++ b/iRLeagueManager/ViewModels/Collections/ObservableViewModelCollection.cs @@ -36,6 +36,7 @@ using System.Windows.Data; using System.Globalization; using System.Collections; +using iRLeagueDatabase.Extensions; namespace iRLeagueManager.ViewModels.Collections { @@ -76,6 +77,9 @@ private IEnumerable CollectionSource private ContainerModelEqualityComparer comparer = new ContainerModelEqualityComparer(); + public bool PreserveViewModels { get; set; } = true; + public bool AllowDuplicates { get; set; } = false; + public ObservableViewModelCollection(bool updateItemSources = true) : base(new ObservableCollection()) { NotifyCollectionActive = true; @@ -148,7 +152,9 @@ public void UpdateSource(IEnumerable collection) { foreach (TModel item in _collectionSource) { - Items.SingleOrDefault(x => comparer.Equals(x.GetSource(), item))?.UpdateSource(item ?? new TModel()); + Items + .Where(x => comparer.Equals(x.GetSource(), item)) + .ForEach(x => x.UpdateSource(item ?? new TModel())); } } } @@ -163,7 +169,7 @@ public void UpdateSource(IEnumerable collection) catch (Exception e) { GlobalSettings.LogError(e); - throw e; + throw; } } @@ -193,86 +199,98 @@ public void UpdateCollection() { if (disposedValue) return; + + if (PreserveViewModels && AllowDuplicates) + { + throw new InvalidOperationException($"{nameof(PreserveViewModels)} and {nameof(AllowDuplicates)} cannot both be set true at the same time"); + } + lock (_collectionSource) lock (TargetCollection) { - try - { - for (int i = 0; i < CollectionSource.Count(); i++) + IEnumerable localSource; + if (AllowDuplicates == false) { - var srcItem = CollectionSource.ElementAt(i) ?? new TModel(); - var trgItem = (i < TargetCollection.Count()) ? TargetCollection.ElementAt(i) : null; - - if (trgItem == null || comparer.Equals(srcItem, trgItem.GetSource()) == false) + localSource = _collectionSource.Distinct(comparer); + } + else + { + localSource = _collectionSource; + } + try + { + if (PreserveViewModels) { - var findTrgItem = TargetCollection.Select((item, index) => new { item, index }).SingleOrDefault(x => comparer.Equals(srcItem, x.item.GetSource())); - if (findTrgItem == null) + for (int i = 0; i < localSource.Count(); i++) { - if (_constructUsing == null) - trgItem = new TViewModel(); - else - trgItem = _constructUsing.Invoke(srcItem); - - trgItem.UpdateSource(srcItem); - _constructorAction?.Invoke(trgItem); - TargetCollection.Insert(i, trgItem); + var srcItem = localSource.ElementAt(i) ?? new TModel(); + var trgItem = (i < TargetCollection.Count()) ? TargetCollection.ElementAt(i) : null; + + if (trgItem == null || comparer.Equals(srcItem, trgItem.GetSource()) == false) + { + var findTrgItem = TargetCollection.Select((item, index) => new { item, index }).SingleOrDefault(x => comparer.Equals(srcItem, x.item.GetSource())); + if (findTrgItem == null) + { + trgItem = ConstructViewModel(srcItem); + if (i < TargetCollection.Count()) + { + TargetCollection.Insert(i, trgItem); + } + else + { + TargetCollection.Add(trgItem); + } + } + else + { + trgItem = findTrgItem.item; + TargetCollection.Move(findTrgItem.index, i); + } + } + + if (srcItem != trgItem.GetSource()) + { + trgItem.UpdateSource(srcItem); + } } - else + + var removeTrgItem = TargetCollection.Skip(_collectionSource.Count()); + foreach (var item in removeTrgItem.ToList()) { - trgItem = findTrgItem.item; - TargetCollection.Move(findTrgItem.index, i); + TargetCollection.Remove(item); } } - - if (srcItem != trgItem.GetSource()) + else { - trgItem.UpdateSource(srcItem); + TargetCollection.Clear(); + foreach (var srcItem in localSource) + { + var trgItem = ConstructViewModel(srcItem); + TargetCollection.Add(trgItem); + } } } - - var removeTrgItem = TargetCollection.Skip(CollectionSource.Count()); - foreach (var item in removeTrgItem.ToList()) + catch (Exception e) { - TargetCollection.Remove(item); + GlobalSettings.LogError(e); + throw; } - } - catch (Exception e) - { - GlobalSettings.LogError(e); - throw e; - } } - CollectionView.Refresh(); - - //IEnumerable except = Items.Select(x => x.GetSource()).Except(_collectionSource, comparer); - //IEnumerable notInCollection = Items.Where(m => except.Contains(m.GetSource())).ToList(); - //IEnumerable notInItems = _collectionSource.Except(Items.Select(x => x.GetSource()), comparer).Where(x => x != null).ToList(); - - //foreach (TModel item in notInCollection) - //{ - // item.Dispose(); - // TargetCollection.Remove(item); - //} - - //foreach (TSource item in notInItems) - //{ - // TModel newItem; - // if (item is TModel) - // { - // newItem = item as TModel; - // } - // else - // { - // newItem = new TModel(); - // newItem.UpdateSource(item); - // _constructorAction?.Invoke(newItem); - // } - // TargetCollection.Add(newItem); - // //OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - //} - - //Sort(); + CollectionView?.Refresh(); + } + + private TViewModel ConstructViewModel(TModel srcItem) + { + TViewModel trgItem; + if (_constructUsing == null) + trgItem = new TViewModel(); + else + trgItem = _constructUsing.Invoke(srcItem); + + trgItem.UpdateSource(srcItem); + _constructorAction?.Invoke(trgItem); + return trgItem; } public void Sort() diff --git a/iRLeagueManager/ViewModels/IncidentReviewViewModel.cs b/iRLeagueManager/ViewModels/IncidentReviewViewModel.cs index fd7c39c..2a938b9 100644 --- a/iRLeagueManager/ViewModels/IncidentReviewViewModel.cs +++ b/iRLeagueManager/ViewModels/IncidentReviewViewModel.cs @@ -123,6 +123,7 @@ public ObservableViewModelCollection private SessionViewModel session; public SessionViewModel Session { get => session; set => SetValue(ref session, value); } + //public long SessionId => SessionId; private MemberListViewModel memberList; public MemberListViewModel MemberList { get => memberList; set => SetValue(ref memberList, value); } @@ -367,13 +368,13 @@ public override void OnUpdateSource() public async Task LoadMemberListAsync() { - if (Model?.Session == null) + if (Model == null) return; try { IsLoading = true; - var result = await LeagueContext.GetModelAsync(Model.Session.SessionId.GetValueOrDefault()); + var result = await LeagueContext.GetModelAsync(Model.SessionId); var members = result.RawResults.Select(x => x.Member); MemberList.SetCollectionViewSource(members); } diff --git a/iRLeagueManager/ViewModels/ReviewsPageViewModel.cs b/iRLeagueManager/ViewModels/ReviewsPageViewModel.cs index 59e41dc..b9abb73 100644 --- a/iRLeagueManager/ViewModels/ReviewsPageViewModel.cs +++ b/iRLeagueManager/ViewModels/ReviewsPageViewModel.cs @@ -94,7 +94,7 @@ public ReviewsPageViewModel() SessionFilter = x => x.ResultAvailable }; currentReviews = new ObservableViewModelCollection(x => - x.Session = SessionSelect?.SessionList.SingleOrDefault(y => y.SessionId == x.Model.Session.SessionId)); + x.Session = SessionSelect?.SessionList.SingleOrDefault(y => y.SessionId == x.Model.SessionId)); AddReviewCmd = new RelayCommand(async o => await AddReviewAsync(), o => SessionSelect?.SelectedSession != null); RemoveReviewCmd = new RelayCommand(async o => await RemoveReviewAsync(o as IncidentReviewModel), o => SelectedReview != null || o is IncidentReviewModel); ReviewNavBar = new ReviewNavBarViewModel() { ReviewsPageViewModel = this }; diff --git a/iRLeagueManager/ViewModels/ScoredResultViewModel.cs b/iRLeagueManager/ViewModels/ScoredResultViewModel.cs index e742d76..9dced10 100644 --- a/iRLeagueManager/ViewModels/ScoredResultViewModel.cs +++ b/iRLeagueManager/ViewModels/ScoredResultViewModel.cs @@ -73,7 +73,10 @@ public ScoredResultViewModel() : base() Model = Template; Session = new SessionViewModel(); CalculateResultsCmd = new RelayCommand(o => CalculateResults(), o => (Session != null && Scoring != null)); - finalResults = new ObservableViewModelCollection(); + finalResults = new ObservableViewModelCollection() + { + PreserveViewModels = false + }; } //public async Task Load() diff --git a/iRLeagueManager/ViewModels/ScoredTeamResultRowViewModel.cs b/iRLeagueManager/ViewModels/ScoredTeamResultRowViewModel.cs index 530ab49..54cd597 100644 --- a/iRLeagueManager/ViewModels/ScoredTeamResultRowViewModel.cs +++ b/iRLeagueManager/ViewModels/ScoredTeamResultRowViewModel.cs @@ -56,7 +56,10 @@ public ICollectionView ScoredResultRows public ScoredTeamResultRowViewModel() { - scoredResultRows = new ObservableViewModelCollection(); + scoredResultRows = new ObservableViewModelCollection() + { + PreserveViewModels = false + }; ScoredResultRows.SortDescriptions.Add(new SortDescription(nameof(FinalPosition), ListSortDirection.Ascending)); } diff --git a/iRLeagueManager/ViewModels/ScoredTeamResultViewModel.cs b/iRLeagueManager/ViewModels/ScoredTeamResultViewModel.cs index 5cec80a..a6df704 100644 --- a/iRLeagueManager/ViewModels/ScoredTeamResultViewModel.cs +++ b/iRLeagueManager/ViewModels/ScoredTeamResultViewModel.cs @@ -52,7 +52,10 @@ public ObservableViewModelCollection(); + teamResults = new ObservableViewModelCollection() + { + PreserveViewModels = false + }; } public bool UpdateSource(ScoredTeamResultModel source) diff --git a/iRLeagueManager/Views/TeamsPageControl.xaml.cs b/iRLeagueManager/Views/TeamsPageControl.xaml.cs index 3138783..8c15ee3 100644 --- a/iRLeagueManager/Views/TeamsPageControl.xaml.cs +++ b/iRLeagueManager/Views/TeamsPageControl.xaml.cs @@ -91,6 +91,7 @@ private async void EditButton_Click(object sender, RoutedEventArgs e) if (content.DataContext is TeamViewModel editVM) { editVM.Model.CopyFrom(teamVM.Model); + await editVM.Refresh(); editWindow.ModalContent = content; if (editWindow.ShowDialog() == true) diff --git a/updates/update.xml b/updates/update.xml index 61278de..d306bab 100644 --- a/updates/update.xml +++ b/updates/update.xml @@ -1,4 +1,10 @@ + + 0.8.4.0 + https://github.com/SSchulze1989/iRLeagueManager/releases/download/v0.8.4-alpha/iRLeagueManager.zip + https://github.com/SSchulze1989/iRLeagueManager/releases/ + true + 0.8.3.0 https://github.com/SSchulze1989/iRLeagueManager/releases/download/v0.8.3-alpha/iRLeagueManager.zip