I'm working on creating a banking application for a school project that uses ASP.net MVC 4. As part of the application we have a set of achievements for the user. When the User completes an achievement, say "Create a Savings Account" they should be rewarded by unlocking the achievement and sending a notification. Here's the Achievement Class:
public class Achievement
{
public string UserID { get; set; }
public AchievementType AchType { get; set; }
public bool Completed { get; set; }
public string Description { get; set; }
public int CountToUnlock { get; set; }
}
public enum AchievementType
{
CREATE_SAVINGS_ACCOUNT, CREATE_GOAL, COMPLETE_GOAL, ACCOUNT_5K,
ACCOUNT_10K, UPDATE_GOAL, ADD_TRANSACTION, SAVE_1K_TOTAL
};
The part I'm struggling with is how to make sure that every user has the same set of achievements and where to trigger those achievements. Whether they should be in an AchievementController or someplace else.
My initial reaction is that you should have a Customer class, which contains a List of that customer's achievements.
Where you trigger the adding of these achievements, depends upon where the logic lives that determines when they've reached one of the goals. For your school project, are you going to rig up a page to simulate this?
Some pseudo to get you started:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Achievement> Achievements { get; set; }
public bool AddAchievement(Achievement newAchievement)
{
// Check to ensure customer doesn't already have the new achievement
// Persist to the database
// Add to local collection
Achievements.Add(newAchievement);
// Send email to customer
// Anything else
// Return
return true;
}
}
Suggestion: Your enum's options should be ProperCase, and put one item on each line. Good to make them adequately descriptive too.
public enum AchievementType
{
CreatedSavingsAccount,
CreatedGoal,
CompletedGoal,
AccountBalance5000,
// [...]
}
Related
I am in a situation where the creation of an entity requires the creation and ID of other entities, however, I am unsure how this is best implemented using MediatR.
For example, say I have a User object...
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public ICollection<Submission> Submissions { get; set; }
}
a Submission object...
public class Submission
{
public int Id { get; set; }
public string Reference { get; set; }
public User User { get; set; }
public ICollection<QuestionsAndAnswer> QuestionAndAnswers { get; set; }
}
and a QuestionAndAnswer object.
public class QuestionAndAnswer
{
public int ID { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
public Submission { get; set; }
}
Each user can have multiple submissions and each submission has multiple questions and answers.
In this instance, the user has gone through a series of questions, provided answers, filled in the final form which provides asks for their contact details and then submitted the information to the API. At this point, I am unsure how to handle the data using MediatR, because the User must be created, with their ID returned as a result to create the submission object which in turn is required so that the questions and answers can be saved.
Should I implement a command which is called CreateUserWithSubmissionQuestionAndAnswersCommand, the handler of which somehow then calls the handlers for the three separate commands and executes them in order, and if so how?
I am just a bit lost on how to implement this scenario as a single HTTPRequest without violating CQRS. The only other way I could think of is implementing separate endpoints which perform each of these tasks individually which requires the client to make and receive three separate calls in succession (CreateUser, CreateSubmission, CreateQuestionAndAnswer)?
Anybody got any ideas?
I usually model the user interactions/user stories as MediatR requests. In most cases this means mapping an single HTTP request to a single MediatR request.
In your case this would be a single request which encapsulates the processing of the submission(s), e.g. ProcessSubmissions. This request would contain properties for the user as well as the submission(s) and the associated question/answer pairs. Then a single handler would persist the individual entities in the correct order.
For completeness, this is what this solution could look like:
public class ProcessSubmission : IRequest
{
public User User { get; set; }
public ICollection<Submission> Submissions { get; set; }
}
public class ProcessSubmissionHandler : IRequestHandler<ProcessSubmission>
{
public async Task<Unit> Handle(ProcessSubmission request, CancellationToken cancellationToken)
{
// Persist the user in the database
var userId = PersistTheUser(request.User);
foreach (var submission in request.Submissions)
{
// Persist each submission with questions and answers
}
}
}
My application has a business logic layer, and a data access layer. I want to give only the data access layer access to the database model. Now, I can easily do this, but then my UI classes cannot access the database classes like Reminder:
namespace Database
{
using System;
using System.Collections.Generic;
public partial class Reminder
{
public long Id { get; set; }
public string Name { get; set; }
public string Date { get; set; }
public string RepeatType { get; set; }
public string Note { get; set; }
public long Enabled { get; set; }
public string SoundFilePath { get; set; }
public string PostponeDate { get; set; }
public Nullable<long> EveryXCustom { get; set; }
public string RepeatDays { get; set; }
public Nullable<long> DayOfMonth { get; set; }
}
}
which is inside the database class library
I use this reminder class to store reminders. In my UI classes I use this class for various reasons.
To make use of this Reminder class, I simply add a reference to the class library that needs to use it. This works fine, but the problem is that every class library that references this, can alter the database like this.
If I'm not using Entity Framework, I could simply have a Reminder class outside the model (because there is no model) and load reminders from the database into that and extract them without using Entity Framework.
Here's an example of why I need to use the Reminder class in my UI classes (this is just a small code sample of one UI class)
This code is inside a timer that ticks every 30 seconds
// We will check for reminders here every 30 seconds.
foreach (Reminder rem in BLReminder.GetReminders())
{
// Create the popup. Do the other stuff afterwards.
if(rem.PostponeDate != null && Convert.ToDateTime(rem.PostponeDate) <= DateTime.Now && rem.Enabled == 1)
{
allowRefreshListview = true;
// temporarily disable it. When the user postpones the reminder, it will be re-enabled.
rem.Enabled = 0;
BLReminder.EditReminder(rem);
MakePopup(rem);
}
else if(Convert.ToDateTime(rem.Date.Split(',')[0]) <= DateTime.Now && rem.PostponeDate == null && rem.Enabled == 1)
{
allowRefreshListview = true;
// temporarily disable it. When the user postpones the reminder, it will be re-enabled.
rem.Enabled = 0;
BLReminder.EditReminder(rem);
MakePopup(rem);
}
}
GetReminders will do get the reminders from the database and put them in reminder objects
using (RemindMeDbEntities db = new RemindMeDbEntities())
{
localReminders = (from g in db.Reminder select g).ToList();
db.Dispose();
}
You can create separate project called i.e. Shared and put there all classes which are used in many projects. Then you need to reference this project by UI project and data access project (and by others which use these classes).
Both will have access to shared classes and UI won't be able to call data access layer directly.
You can also create interface outside of data access layer but if your classes are DTOs (Data Transfer Object) first option will be better.
If im not using the entity framework, i could simply have a reminder
class outside the model
You could create an interface instead of a class outside of the model in a shared assembly:
public interface IReminder
{
public long Id { get; }
public string Name { get; }
public string Date { get; }
public string RepeatType { get; }
public string Note { get; }
public long Enabled { get; }
public string SoundFilePath { get; }
public string PostponeDate { get; }
public Nullable<long> EveryXCustom { get; }
public string RepeatDays { get; }
public Nullable<long> DayOfMonth { get; }
}
Your Entity can than implement the interface:
public partial class Reminder : IReminder
{
//...
}
Maybe you want to make your Entities only internal visible and expose public service methods like IEnumerable<IReminder> GetReminders()
So I have a controller which updates just 2 fields in a db entry, however that entry is linked to two other tables, I know it's a bad explanation but sometimes it works and sometimes it doesn't and I cant identify what's different between submissions since no code changes.
Error
Controller
GroupFitnessSession session = unitOfWork.GroupFitnessSessionRepository.GetById(item.GroupFitnessSessionId);
session.IsConfirmed = true;
session.Attendees = item.Attendees;
unitOfWork.GroupFitnessSessionRepository.Update(session);
There are other fields to the Models that i've left out, but non of them are the same name or something to these oens
Models
public class GroupFitnessSession
{
public string GroupFitnessSessionId { get; set; }
[Required]
public virtual Trainer Trainer { get; set; }
[Required]
public virtual Location Location { get; set; }
}
public class Location
{
public string LocationId { get; set; }
public Location()
{
GroupFitnessSession = new HashSet<GroupFitnessSession>();
}
public ICollection<GroupFitnessSession> GroupFitnessSession { get; set; }
}
public class Trainer
{
public Trainer()
{
GroupFitness = new HashSet<GroupFitnessSession>();
}
public ICollection<GroupFitnessSession> GroupFitness { get; set; }
If you need any other information feel free to ask.
This is just confusing me too much, any advice would be appceiated
EDIT: showing that Location and Trainer are not empty objects
As you can see the auto generated Properties from EF aswell as the propertiy I am trying to update
This will work if you change the first line of the controller to:
GroupFitnessSession session = unitOfWork.GroupFitnessSessionRepository.GetById(item.GroupFitnessSessionId).Include(s => s.Trainer).Include(s => s.Location);
I'm trying to add some architecture to my projects and enrich my models.
I started with CQS (implementation similar to that one: CQS-Sample) and here's my first problem.
Let's say I have two classes like these below:
public class Network
{
public int Id { get; set; }
public User User { get; set; }
private IQueryFactory _queryFactory { get; set; }
public Network(IQueryFactory queryFactory)
{
_queryFactory = queryFactory;
}
public void AddUser(User user)
{
if(this.User == null && user != null)
{
userHasUniqueEmail(user);
this.User = user;
}
}
private void userHasUniqueEmail(User user)
{
bool isUnique = _queryFactory.ResolveQuery<INewUserUniqueQuery>().Execute(user.Email);
if (!isUnique)
{
throw new ArgumentException("E-mail is not unique");
}
}
}
public class User
{
public int Id { get; set; }
public string Email { get; set; }
}
Network object can have User, but I need to check first in database that given e-mail doesn't already exists or do some other checkings, so my commands will be executed successfully.
By adding user I mean adding completely new User to database.
Would it be correct way to do this?
You can do it the way you do it now and it's ok.
Another option is to make this Validation in Contoller. Then you should use Remote attribute. And Move your IsEmailUnique(string mail) method to Controller.
If you want to know how you can do it with email check - this question will help you.
Over the past two years I developed apps for the CF .NET 3.5 to be runned on warehouse's portable device(windows mobile).
From the beginning I just jumped into the process and made a lot of mistakes that I'm gradually correcting. What has came out are apps made in this way:
a main form to start the whole process which automatically creates a data-form, that will stay alive for the whole time. This data-form will keep all the datas that the user will insert or request from the server. The other forms are basically views of the data with methods to manipulate them.
It works but...am I doing this in the right way? Or maybe am I missing something really fundamental?
So, you created a data form, and you are using it like RAM. You never display the data, you simply store it there to access.
If someone ever has to take over your job (like you leave the company or die), they are going to hate you so bad.
A better technique would be to create a Class that houses all of this data.
The good part is, since you already have a data form, you probably already know how everything is organized!
Now, just use that knowledge of your data to create your class that you can read and write to.
If you have groups of similar items, create other classes that your main class will contain.
If you have several of these similar items, create publically accessible Lists of these items.
Make it as dead simple or as complex as you'd like!
Consider these classes, which are all generic enough to modify however you would need and demonstrate some extras added:
public class DataForm {
private GroupedItem m_item2;
public event EventHandler Item2Changed;
public DataForm() { // this is your constructor
Item1 = new GroupedItem();
Item2 = new GroupedItem();
ItemCollection = new GroupCollectionItems("Group1");
}
public float Value1 { get; set; }
public float Value2 { get; set; }
public GroupedItem Item1 { get; set; }
public GroupedItem Item2 {
get { return m_item2; }
set {
if (m_item2 != value) {
m_item2 = value;
if (Item2Changed != null) {
Item2Changed(this, EventArgs.Empty); // notify whoever is listening for the change
}
}
}
}
public GroupCollectionItems ItemCollection { get; set; }
}
public class GroupedItem {
public GroupedItem() { // this is your constructor
}
public string Name { get; set; }
public object Value { get; set; }
}
public class GroupCollectionItem {
private GroupCollectionItem() { // this is your constructor
}
public static GroupCollectionItem Create(string groupName, string itemName, object itemValue) {
var item = new GroupCollectionItem() {
Group = groupName,
Name = itemName,
Value = itemValue
};
return item;
}
public string Group { get; private set; }
public string Name { get; private set; }
public object Value { get; set; }
}
public class GroupCollectionItems : List<GroupCollectionItem> {
public GroupCollectionItems(string name) { // this is your constructor
Name = name;
}
public string Name { get; private set; }
}