As part of my objective of learning a new skill at work I am attempting to develop an employee management system in ASP.NET MVC (MVC 4).
I am trying to follow the convention of performing all validation at the model level (not only because this is what I have read is recommended but also as there is talk of a desktop app that may use parts of the model so I want to ensure any constraints are enforced in that app too!).
My issue is, I have some data on the Person class (RemainingHoliday). When create a HolidayRequest I want to ensure that the request is not for a greater number of days than the person has remaining.
How would I go about doing this? I know that I can create my own validation rules by extending the ValidationAttribute, but how would I get from the HolidayRequest class to the Person class within here?
A snippet of the models:
public class Person
{
public string PersonID { get; set; } // this is populated with Users AD Guid
public string HolidayEntitlement { get; set; }
public virtual ICollection<HolidayRequest> Holidays { get; set; }
public int TotalEntitlement(int year = -1)
{
return this.HolidayEntitlement + this.HolidayAdjustments.Where(a => a.LeaveYear.Year == year).Sum(a => a.Adjustment);
}
public int RemainingHoliday(int year = -1)
{
return this.TotalEntitlement(year) - this.Holidays.Where(h => h.Start.Year == year).Where(h => h.Status != HolidayStatus.Rejected).Sum(h => h.Duration);
}
}
public class HolidayRequest
{
public string HolidayId { get; set; }
public DateTime Start { get; set; }
public DateTime Finish { get; set; }
public int Duration { get; set; } // This cannot be greater than Person.RemainingHoliday
}
I would really appreciate any pointers or samples for this, or perhaps I am trying to be too ideal and this cannot be done in the model?
It seems that you are missing a reference to the person requesting the holiday in the HolidayRequest object. I would have expected to see a Person or PersonId in the HolidayRequest object. Once there, you could compute the difference between Duration and RemainingHoliday.
Related
I am having some issues figuring out how the correct way to properly model a many-to-many relationship in my realm, namely around the fact that realm objects are always live.
So the model in question here revolves around two objects: Event and Inventory. An Event can have multiple inventory items assigned to it (think chairs, plates, forks, etc.), and an inventory item can be assigned to multiple events. When we assign it to an event we define how many of said item we want to assign to the event. However this is where the problem arises, since realm objects are always live and the object types are the same, whatever data Events has will affect my inventory data row as well.
Big picture is that I want to show how many items are assigned for each up coming event when I go into my Inventory detail view. So for example I may have 50 total chairs, I've assigned 40 for an event tomorrow, this means I cannot assign another 20 if someone tried to schedule an event that day as well.
My Realm objects look as follows:
public class Event : RealmObject
{
[PrimaryKey]
public string EventId { get; set; }
[Indexed]
public string VenueId { get; set; }
[Indexed]
public string Name { get; set; }
public DateTimeOffset DateOfEventUTC { get; set; }
public IList<Inventory> Items { get; }
}
public class Inventory : RealmObject
{
[PrimaryKey]
public string InventoryId { get; set; }
[Indexed]
public string VenueId { get; set; }
public Category Category { get; set; }
public int Count { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[Backlink(nameof(Event.Items))]
public IQueryable<Event> Events { get; }
}
I then try to do what I want (namely showing how many of the item are assigned for that event) in my VM as so:
var item = unitOfWork.InventoryRepository.GetById(inventoryId);
var nextMonth = DateTime.UtcNow.AddMonths(1);
AssignedEvents = item.Events
.Where(x => x.DateOfEventUTC >= DateTime.UtcNow && x.DateOfEventUTC <= nextMonth)
.ToList()
.Select(x => new AssignedEventModel
{
DateOfEventUTC = x.DateOfEventUTC.DateTime,
Name = x.Name,
AssignedItems = x.Items.First(z => z.InventoryId == inventoryId).Count
})
.ToList();
Unfortunately, this is where the problem arises. I tried applying the [Ignored] tag as was recommended in the realm docs so that the item will no longer be persisted. This unfortunately did not solve my issue. I am still new to realm and I am much more familiar with SQL than NoSQL
I struggle to see how this could work in SQL either, but I'm not an expert in that so I may miss some details that would allow this to work in the way you structured it.
Coming back to our case: the problem has little to do with Realm being live, but more to do with the way you structured your domain models.
If you use the same "Inventory" model to do 2 things:
keep track of the total amount of each item
keep track of the amount of each inventory item used in a specific event
you'll have problems with what Count really represents.
Creating a third model would solve all your problems.
Inventory => for the overall amount of an item
Event => representing the event and all its data
EventInventory => representing the amount of an item used in that event
Not having much information about your project and your other models (see AssignedEventModel etc) I could suggest something along these lines
class Event : RealmObject
{
[PrimaryKey]
public string EventId { get; set; }
// ... other fields you need ...
public DateTimeOffset DateOfEventUTC { get; set; }
[Backlink(nameof(EventInventory.Event))]
public IList<EventInventory> Items { get; }
}
class EventInventory : RealmObject
{
public Inventory Inventory { get; set; }
public int Count { get; set; }
public Event Event { get; set; }
}
class Inventory : RealmObject
{
[PrimaryKey]
public string InventoryId { get; set; }
// ... other fields you need ...
public int TotalCount { get; set; }
[Backlink(nameof(EventInventory.Inventory))]
public IQueryable<EventInventory> EventInventories { get; }
}
Then in your Inventory's VM
var inventory = unitOfWork.InventoryRepository.GetById(inventoryId);
var inUse = inventory.EventInventories
.Where(x => /*...*/)
.Sum(x => x.Count);
// your databind count that want to show under Inventory's View
remainingCount = inventory.TotalCount - InUseCount;
So basically, now you can calculate how much is left available of a certain InventoryItem in a certain time frame. With these models you should be able to create your AssignedEventModel if you need to.
I hope this helps.
On a side node, I noticed that you are using unitOfWork and repository pattern (at least, so it seems). Although it may look like a great idea, it is generally discoraged to be used when working with Realm. This is simply because you are going to miss out on some of the powerful feature of Realm.
You can read more about this here in the "Repository" section of the answer.
This is a C# Question, using .NET framework built on Asp.NET Boilerplate.
Again, to re-emphasis the question being asked is "HOW...", so if an answer that was provided was a url link or a descriptive explanation on how something was supposed to be done, i would very much appreciate it. (Dont answer questions on how to tie shoelace by showing a picture of a tied shoe, nor do you answer "how to fish" by showing a recording of someone fishing...)
Since the question is pretty basic (i don't need to rephrase/repeat the header again), i'll give an example.
If i have a Forum service, and i create a class to load a Thread. Inside that thread class should be some sort of collection, array, list, or even a dbset of Post that is pulled on construct.
[Table("Thread", Schema = "dbo")]
public class ThreadModel
{
[Key]
public long Id { get; set; }
public string Title { get; set; }
//Idea 1
//Value should automatically be pulled and cached the moment class connects to database
public Post[] Posts { get; set; }
//Idea 2
//Post has a constructor to return all post that matches a thread id. While new tag keeps the return value constantly refreshed.
public Post[] Posts { get { return new Post(this.Id) } }
//Idea 3
//Not sure how collection is supposed to work. Does it automatically just pull or will i need to make a method to request?
public virtual ICollection<Post> Posts { get; set; }
//Example constructor
//When connected to database key-value pairs that match database labels will automatically get stored in class
protected ThreadModel()
{
//Idea 1-A
//Should be a value of null or empty if database yields no results
Posts = new Post();
}
public ThreadModel(int threadid) : this()
{
//Idea 1-A
Id = threadid;
//new Post => returns all posts in db
//Posts default value is all post in db
Posts = Posts.Select(post => post.threadid == this.id)
//Idea 3-A
Posts = Posts.Get(post => post.threadid == this.id)
//Idea 4
Posts = new Posts().GetThread(threadid);
}
}
Side questions
If all entities are created by inheriting Entity then at what point am i exposed to EntityFramework and DbContext?
I love this example here, submitted by a user as they attempt to connect ABP to their database. But their example doesn't show parent/child resources. I'm unable to find the guide they used to create that, and how it relates back to using ABP to fetch EntityFramework's DbContext example
How does this work? I'm unable to find instructions or explanation for this? (What am i to enter into google to get answers on these mechanics?)
[Table("AbpItems")]
public class Item : Entity
{
[ForeignKey("PostId")]
public Post Post { get; set; }
public int PostId { get; set; }
}
How does this integrate into/with abp's EntityFramework?
Where am i supposed to be creating my Database Table/Class? The project follows the Core.csproj, Application.csproj, and EntityFramework.csproj assembly layout. But it seems like every example is creating the classes at different stages or locations of the solution.
use GetAllIncluding. See https://github.com/aspnetboilerplate/aspnetboilerplate/issues/2617
Here's a complete solution ;
namespace EbicogluSoftware.Forum.Threads
{
[Table("Threads")]
public class Thread : FullAuditedEntity
{
[Required]
[StringLength(500)]
public virtual string Title { get; set; }
[Required]
[StringLength(2000)]
public virtual string Text { get; set; }
public virtual List<Post> Posts { get; set; }
public Thread()
{
Posts = new List<Post>();
}
}
[Table("Posts")]
public class Post : FullAuditedEntity
{
[Required]
[StringLength(2000)]
public virtual string Text { get; set; }
}
public class ThreadDto
{
public string Title { get; set; }
public string Text { get; set; }
public List<PostDto> Posts { get; set; }
public ThreadDto()
{
Posts = new List<PostDto>();
}
}
public class PostDto
{
public string Text { get; set; }
}
public class ThreadAppService : IApplicationService
{
private readonly IRepository<Thread> _threadRepository;
public ThreadAppService(IRepository<Thread> threadRepository)
{
_threadRepository = threadRepository;
}
public async Task<List<TenantListDto>> GetThreads()
{
var threads = await _threadRepository.GetAllIncluding(x => x.Posts).ToListAsync();
return threads.MapTo<List<TenantListDto>>();
}
}
}
Where am i supposed to be creating my Database Table/Class?
You can create them in YourProject.Core.proj
I have a simple test solution which consists of two projects (a 'business' layer and a Data Access layer) using Catel to tie the two together - works fine, no problems.
However, have been reading about how useful AutoMapper can be for helping to move data around such a setup by allowing easy population of DTO's and decided to give it a look...that's when my problems started!
I'm using Entity Framework 6.1, VS 2013 Express for Desktop and accessing a SQL Server Express 14 db - no problems with data retrieval and data displays correctly in my views.
AutoMapper was added using NuGet.
In order to use AutoMapper I've set up the following in my App.xaml.cs
private void InitializeAutomapper()
{
Mapper.CreateMap<Result, ResultDto>();
Mapper.AssertConfigurationIsValid();
}
This code is the first item called inside my 'OnStartup'.
A service in my business layer makes a call to the Data Access layer and retrieves a list of Result entites.
Subsequently, I take a single entity from this list and use that in the AutoMapper mapping call.
I'm trying to populate a resultDTO from this single entity, using the following
Result res = ResultList.First();
ResultDto resultDTO = Mapper.Map<Result, ResultDto>(res);
'res' is correctly populated with data but resultDTO is filled with the default values for the individual data types (in = 0, string = null, DateTime = {01/01/0001 00:00:00}) ie; no values are mapped from the source to the destination.
There are References in both projects to AutoMapper and AutoMapper.Net and no errors are raised - it just doesn't work as advertised...
I'm not slagging off the software, just asking what I'm doing wrong!
I realise there isn't much code to work on here but, in truth, what is posted here is pretty much all I've added to try out AutoMapper. I can see, conceptually how useful it could be - I just need to figure out how to make it happen so any help/comments gratefully received...:)
EDIT
#Andrew, as requested -
Result Class:
public partial class Result
{
public int Div { get; set; }
public System.DateTime Date { get; set; }
public string HomeTeam { get; set; }
public string AwayTeam { get; set; }
public int FTHG { get; set; }
public int FTAG { get; set; }
public string FTR { get; set; }
}
ResultDTO Class:
public class ResultDto
{
int Div { get; set; }
DateTime Date { get; set; }
string HomeTeam { get; set; }
string AwayTeam { get; set; }
int FTHG { get; set; }
int FTAG { get; set; }
string FTR { get; set; }
// Added tonight to try and get it to work
public ResultDto()
{
Div = 0;
Date = DateTime.Now;
HomeTeam = null;
AwayTeam = null;
FTHG = 0;
FTAG = 0;
FTR = null;
}
}
#stuartd, the following is used to retrieve the ResultList from which Result is obtained:
// Produce a list of DataLayer.Result entities.
var ResultList = (from x in dbContext.Results.Local
where x.HomeTeam == team.TeamName.ToString() || x.AwayTeam == team.TeamName.ToString()
orderby x.Date
select x).ToList();
Please note 'team.Teamname' is passed into the above from an external source - seems to be working fine.
So to sum up -
I produce ResultList as a list of Result entities.
Fill Result with the first entity in the list.
Try to map this Result entity to ResultDTO
Fail :(
Hope this helps!
By default, class members are declared private unless otherwise specified so the ResultDto properties aren't visible outside of the class.
public class ResultDto
{
int Div { get; set; }
....
}
needs to be
public class ResultDto
{
public int Div { get; set; }
....
}
AutoMapper can work out the type you are mapping from from the arguments provided. Try this:
ResultDto resultDTO = Mapper.Map<ResultDto>(res);
UPDATE
This is wrong, or at least won't help. We need to see the source and destination classes as mentioned in the comments.
Using the following code, assume I have 5 different types that I might receive in the variable type. Instead of writing 5 conditional statements, is there a way to write one and use the variable "type" to dictate what the model is, in this case "CommentVote?" Or is this more a deficiency in the way I've designed the data model with each of those 5 things having a "vote" model?
if (type == "comment")
{
CommentVote voteObj = db.CommentVotes
.Where(x => x.UserID == UserID && x.CommentID == id)
.SingleOrDefault();
if (voteObj != null)
{
voteObj.Vote = vote;
db.SaveChanges();
}
else
{
CommentVote c = new CommentVote {
CommentID = id, UserID = UserID, Vote = vote, DateCreated = DateTime.Now
};
db.CommentVotes.Add(c);
db.SaveChanges();
}
count = (db.CommentVotes.Count(x => x.CommentID == id && x.Vote == true) - db.CommentVotes.Count(x => x.CommentID == id && x.Vote == false));
}
Magic Code: The stuff I would love to be able to do.
var modelName = "";
var modelOtherName = "";
if (type == "comment") {
modelName = CommentVote;
modelOtherName = CommentVotes;
}
modelName voteObj = db.modelOtherName
.Where(x => x.UserID == UserID && x.CommentID == id)
.SingleOrDefault();
Update: I'm beginning to think my model may be crap based on some of the reading referenced bellow. So I am including some of that as a reference. Let me know if that's the problem I should be trying to solve.
public class CommentVote
{
public int CommentVoteID { get; set; }
public bool Vote { get; set; }
public DateTime DateCreated { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public int CommentID { get; set; } //This row changes from model to model
public virtual Comment Comment { get; set; } //This row changes from model to model
}
I have a handful of models that are almost identical.
As I understand you question, it more database architecture-related.
If those kind of votes are not very different from each other (in terms of properties) I woldn't use different tables for them. Instead create one Vote table with Type column and (as in the example you provided) nullable column for CommentID.
Then you can use class inheritance to reflect your votes (Vote base class and CommentedVote child class).
Table Per Hierarchy Inheritance in Entity Framework
Update:
Best is not to repeat the same propertieses in all classes. You just use inharitence like this:
public abstract class Vote
{
public int VoteID { get; set; }
public bool isVote { get; set; }
public DateTime DateCreated { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public int VoteType { get; set;} //this property specifies type of vote (e.g. VoteType=1 for CommentedVote )
}
public class CommentVote : Vote
{
public int CommentID { get; set; }
public virtual Comment Comment { get; set; }
}
public class OtherVote : Vote
{
public int OtherID { get; set; }
public virtual Other Other { get; set; }
}
In this very good blog post you can find all possible approches. The one I'm writing about is called Table per Hierarchy (TPH).
You can absolutely reduce the code to a single statement assuming that you perform the same actions and set the same data. In this case, you should have an interface that contains the common actions and data and an object factory to instantiate the correct object based on the type.
You could do it if you implement the Factory pattern with reflection, a very basic example is shown here.
In a nutshell what you do is this: Since you have 5 different types that it could be, you would make 5 different classes that each implement a specific interface. You then create the factory class to use reflection to grab the class that is the most appropriate for your situation (be it with a straight-up class name, like in the example, or with an Attribute over the class, such as here). The factory returns an instance of that interface, which you would then just invoke the exposed method from the interface to do all of this for you.
The best part of this is that if you ever need to make another type, all you'd have to do is add another class with that attribute/name that you would be searching for in the factory. None of your other code would need to be affected, thus making you compliant with the Open/Closed Principle.
I've got three classes.
Event > Workshop > Workshop Times
I'm currently looking for best way of inserting records into the Workshop Times, this is running through code first using ICollections.
Looking for something along the lines of this, but I know it doesn't work:
//Create connection
var db = new Context();
var Event = db.Events
.Include("Workshops")
.Include("Workshops.Times")
.Where(ev => ev.GUID == EventGUID).FirstOrDefault();
Event.Workshops.Add(new Workshop
{
Name = tbWorkshopName.Text,
Description = tbWorkshopDescription.Text,
Times.Add(new WorkshopTime{
//Information for times
})
});
db.SaveChanges();
Chopped down classes:
public class Workshops{
public int id { get; set; }
public string name { get; set; }
public ICollection<WorkshopTimes> Times{get;set;}
}
public class Events {
public int id { get; set; }
public string name { get; set; }
public ICollection<Workshops> WorkShops { get; set; }
}
public class WorkshopTimes {
public int id { get; set; }
public DateTime time { get; set; }
}
You are definitely on the right track with your query, however your include statements appear incorrect. From your model I would expect:
var Event = db.Events
.Include("WorkShops")
.Include("WorkShops.events")
.Where(ev => ev.GUID == EventGUID).FirstOrDefault();
Note this uses the property names not the types. This will ensure that the entities in the listed nav properties will be included in the result.
In addition you can use a lambda to do the same thing (but its typesafe)
Check out here for how to do a very similar scenario to yours:
EF Code First - Include(x => x.Properties.Entity) a 1 : Many association
or from rowan miller (from EF team)
http://romiller.com/2010/07/14/ef-ctp4-tips-tricks-include-with-lambda/
And make sure you are using System.Data.Entities for lambda based includes ( Where did the overload of DbQuery.Include() go that takes a lambda? )