I have a Business Object (Domain Object) representing an employee's shift timings. Its name is EmployeeWorkShift.
using System;
namespace BusinessObjects
{
public class EmployeeWorkShift
{
public long EmployeeWorkShiftId { get; set; }
public long EmployeeId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public TimeSpan StartTime { get; set; }
public TimeSpan EndTime { get; set; }
}
}
I have a Repository to create, read, update and delete this Business Object in database. Its name is IEmployeeWorkShiftRepository.
I have a Service which has methods to perform operations with this Business Object. Its name is IEmployeeWorkShiftService.
The User Interface call the Service methods for different events:
To retrieve all EmployeeWorkShift objects of an employee, it calls List<EmployeeWorkShift> GetEmployeeWorkShifts(long employeeId); method
To retrieve a specific EmployeeWorkShift object, it calls EmployeeWorkShift GetEmployeeWorkShift(long employeeWorkShiftId); method
To insert a specific EmployeeWorkShift object, it calls EmployeeWorkShift InsertEmployeeWorkShift(EmployeeWorkShift employeeWorkShift); method
To update a specific EmployeeWorkShift object, it calls EmployeeWorkShift UpdateEmployeeWorkShift(EmployeeWorkShift employeeWorkShift); method
To delete a specific EmployeeWorkShift object, it calls void DeleteEmployeeWorkShift(EmployeeWorkShift employeeWorkShift); method
Now in the User Interface, for retrieve/insert/update, the user wants to use some specific formats for dates and times of EmployeeWorkShift object.
One way to solve this issues, is to add 4 string properties in EmployeeWorkShift object which contains the dates and times in specific formats user desires:
using System;
namespace BusinessObjects
{
public class EmployeeWorkShift
{
public long EmployeeWorkShiftId { get; set; }
public long EmployeeId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public TimeSpan StartTime { get; set; }
public TimeSpan EndTime { get; set; }
public string StartDateString { get; set; }
public string EndDateString { get; set; }
public string StartTimeString { get; set; }
public string EndTimeString { get; set; }
}
}
So in User Interface I don't use the original 4 properties of dates and times and instead use the new 4 string properties.
In Service method for retrieve, once I get data from Repository, I translate the original 4 properties of dates and times retrieved from database into specific formats and populate the new 4 string properties.
In Service method for insert/update, I translate the new 4 string properties into original 4 properties of dates and times before calling Repository.
This looks a crude solution to me. Is there a better way to solve this issue?
In my view, formatting Dates for display purposes is a presentation concern, not a business logic concern. I would imagine also that the formatting for dates affects all dates that the user sees, not only the dates related EmployeeWorkShift, so your approach would require extending every single entity which contains dates with the string property and applying the logic everywhere.
What I would do is have the business objects working with DateTime only both for reads and writes. Then in the presentation layer I would have a DateTime formatter which would accept the DateTime and a Format parameter. The Format parameter could be retrieved from User settings or obtained from the selected Culture for example.
So, you'd have the concerns separated into 3 parts:
Business logic works with DateTimes always. This will simplify the business logic layer and avoid mistakes
A single Formatter function to format any DateTime for display regardless of the business object it belongs to (you'll need a Parser function too).
A single way to retrieve the format, decoupled from all business objects and dates presented in the UI, so you can easily replace how you obtain it (combo box on the page, from the Culture in the browser or system, from user settings, etc).
It is possible to create one field which will store format of your DateTime or TimeSpan. By using this property you can format your value for presentation layer. Let's call this property as DateFormat.
Then at runtime you can decide what formatter should be used to format your value. It can be done through Strategy pattern. Strategies of formatting will be stored in collection and you can get instance of strategy by using Factory pattern.
So let's see an example of implementation. This is property DateFormat:
public enum DateFormat
{
Date, Time
}
And your class EmployeeWorkShift:
public class EmployeeWorkShift
{
// the other code is omitted for the brevity
public DateFormat DateFormat { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
// the other code is omitted for the brevity
}
And then you can create abstract class DateFormatter which will define behaviour for all derived classes.
These derived classes will format values:
public abstract class DateFormatter
{
public abstract string Format(string dateTime);
}
public class DateTimeFormatter : DateFormatter
{
public override string Format(string dateTime) =>
DateTime.Parse(dateTime).ToString();
}
public class TimeSpanFormatter : DateFormatter
{
public override string Format(string dateTime) =>
TimeSpan.Parse(dateTime).ToString();
}
And we need a factory which will return instance of formatter by DateFormat:
public class DateFormatFactory
{
private Dictionary<DateFormat, DateFormatter> _formattersByDateFormat =
new Dictionary<DateFormat, DateFormatter>()
{
{ DateFormat.Date, new DateTimeFormatter() },
{ DateFormat.Time, new TimeSpanFormatter() }
};
public DateFormatter GetInstance(DateFormat dateFormat) =>
_formattersByDateFormat[dateFormat];
}
And this is a mock method of getting shifts:
IEnumerable<EmployeeWorkShift> GetShifts() => new List<EmployeeWorkShift>
{
new EmployeeWorkShift { DateFormat = DateFormat.Date, StartDate="2022-12-25" },
new EmployeeWorkShift { DateFormat = DateFormat.Time, StartDate="6:12:14:45" }
};
And another method to show formatted values:
void ShowFormattedValues()
{
DateFormatter dateFormatter;
DateFormatFactory dateFormatFactory = new DateFormatFactory();
foreach (EmployeeWorkShift shift in GetShifts())
{
dateFormatter = dateFormatFactory.GetInstance(shift.DateFormat);
Console.WriteLine("formatted value: "
+ dateFormatter.Format(shift.StartDate));
}
}
And you can call it like this:
ShowFormattedValues();
So we are getting strategy to format values based on DateFormat property. And the pattern name is Strategy.
So our code follows Open Closed principle of SOLID principles. When you will want to add new strategy of formatting you will need a new class of FooFormatter.
In addition, there is no need to write twice name in method and service. You can have:
public interface IEmployeeWorkShiftRepository
{
string GetById();
string GetByShiftId();
void Insert();
void Update();
void Delete();
}
and call it like this:
employeeWorkShiftRepository.GetById(); //
Related
I have a few EF model classes that I want to create. Each class has a few common properties that I want to set before inserting a new entity, for example:
public partial class BlogPost {
public DateTime CreatedTime { get; set; }
public string CreatorName { get; set; }
public string PostTitle { get; set; }
public string PostText { get; set; }
}
public partial class Comment {
public DateTime CreatedTime { get; set; }
public string CreatorName { get; set; }
public string CommentText { get; set; }
}
...
When I create these classes, I'm instantiating them like so:
var blogPost = new BlogPost {
CreatedTime = DateTime.UtcNow,
CreatorName = creatorName,
PostTitle = postTitle,
PostText = postText,
};
var comment = new Comment {
CreatedTime = DateTime.UtcNow,
CreatorName = creatorName,
...
};
...
I want to create a method to automatically set some of the common properties so I don't need to manually type them out for each class with the same properties. Since they don't extend the same class or implement the same interface, I'm wondering how this can be achieved. My first thought was to use a generic method; however, I don't know if there's a way to specify what properties the generic type should have without them extending the same class (similar to TypeScript's "duck typing"). My desired method looks something like this:
public void SetInitialProperties<T>(T dbEntity, DateTime createdTime, string creatorName) where T : ??? {
dbEntity.CreatedTime = createdTime;
dbEntity.CreatorName = creatorName;
}
...
var blogPost = new BlogPost { PostTitle = postTitle, PostText = postText };
SetInitialProperties(blogPost, createdTime, creatorName);
Worst case scenario if I can't use a generic, I could always use dynamic; however, I'd like to keep type checking if possible.
You can achieve what you want using reflection. You can pass in an object and resolve it's type, then get all the public properties of that given type and find if you have one called CreatedTime for example. Then you can set the value of the given property on the passed dbEntity object. However, I do not recommend this solution:
public void SetInitialProperties(object dbEntity, DateTime createdTime, string creatorName) {
// get the passed object's properties and find the one called CreatedTime
var createdTimePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatedTime").FirstOrDefault();
// below line is equal to: dbEntity.CreatedTime = createdTime;
createdTimePropertyInfo.SetValue(dbEntity, createdTime);
var creatorNamePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatorName").FirstOrDefault();
creatorNamePropertyInfo.SetValue(dbEntity, creatorName);
}
You would be better off on the long run by creating a common interface or even an abstract base class so you don't have to implement CreatedTime and CreatorName and other properties for every EF model. It would look like the following:
public interface IUserEntry
{
DateTime CreatedTime { get; set; }
string CreatorName { get; set; }
}
public abstract class UserEntryBase : IUserEntry
{
public DateTime CreatedTime { get; set; }
public string CreatorName { get; set; }
}
public partial class BlogPost : UserEntryBase
{
public string PostTitle { get; set; }
public string PostText { get; set; }
}
public partial class Comment : UserEntryBase
{
public string CommentText { get; set; }
}
And your SetInitialProperties would be pretty simple:
public void SetInitialProperties(IUserEntry dbEntity, DateTime createdTime, string creatorName)
{
dbEntity.CreatedTime = createdTime;
dbEntity.CreatorName = creatorName;
}
Once you develop onto an interface, you achieve much more flexibility than by using reflection or a dynamic type, since you get the compile-time checking that was mentioned before me and you can see the common properties of your models.
You can't do that in C# because C# uses a nominal type system and not a structural type system.
For your particular case you have to come up with an interface that contains the properties in common and which will be implemented by both entities, then use that new interface as you generic function parameter constraint.
If you're absolutely sure the properties will have the same name, you could pass a dynamic to set property values. However, this prevents any compile-time checking of the typing, so if you accidently pass an incompatible type it won't be caught until runtime.
public void SetInitialProperties(dynamic dbEntity, DateTime createdTime, string creatorName) {
dbEntity.CreatedTime = createdTime;
dbEntity.CreatorName = creatorName;
}
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()
I know there's data annotation to validate data such as [Required], [Range()] and so on. Not to mention the unobtrusive validation that makes it easier in the client side. Many thanks to them.
But what about if you need to validate the value that depends on the entity's property value? I have this scenarion, say:
My models:
public class Schedule
{
public int Id { get; set; }
public DatetimeOffset StartDate { get; set; }
public DatetimeOffset EndDate { get; set; }
}
Now in a form,
<input type="text" name="StartDate" />
<input type="text" name="EndDate" />
How would you validate that the EndDate should not be less than the StartDate? Is there a built-in attribute in data annotation for that? Or should make a custom one? It would be great if it would make use of the unobstrusive validation by microsoft.
Here's another scenario:
What if you would do validation that depends on the value that is saved in the db? Say,
public class Bag
{
//constructor
public int Capacity { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
//constructor
public string Description { get; set; }
public virtual ICollection<Bag> Bags { get; set; }
}
That if you would do validation on the Items being added to the Bag but if the user tries to input beyond the limit of the Bag's Capacity, should show the validation error.
Is this possible?
I'm currently using ASP.NET MVC 4. EF5 Code first approach.
The first approach is to implement IValidatableObject:
public class Schedule : IValidatableObject
{
public int Id { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (StartDate > EndDate)
{
yield return new ValidationResult("End date must be greater than start date.", new [] { "EndDate"});
}
}
}
It will be executed automatically during model binding on server side.
If you want to validate on client side, you also have options. One of them is remote validation. You can read about it here. To summarize: You have to create constroller and action, that takes validated values and returns if it is valid or not. It can take more than one value. For example ID and username if you want to check uniqueness.
Obviously sending values to server is not necessary in date comparison. You can implement your own validation attribute to handle comparison on client side. Someone tried to implement it here.
I have some code that takes a list made up of custom class objects (called Payments) and sorts them by date, as below:
payments.Sort(delegate(Payments p1, Payments p2) { return p1.GetDate().CompareTo(p2.GetDate()); });
the GetDate() method and the payment class is below:
public class Payments
{
public string Date { get; set; }
public string Payment { get; set; }
public string Reference { get; set; }
public decimal Amount { get; set; }
public DateTime GetDate()
{
return DateTime.Parse(this.Date);
}
}
Once the list is sorted I manually go through each one and compare the date on it to the date on the next one. If they are different then nothing happens, if they are the same then I merge the data in the Payments together into a single new Payment. I then remove the two payments that were being compared and then add in the new one, the list is then resorted and continued until the list is unique by date.
Up until very recently this has been working fine, with no issues. However from today there have been multiple cases of it erroring with the message "Failed to compare two elements in the array".
I have looked around for this but I dont feel that i know enough about what could be causing it to comfortably make changes to my code. Can someone help me understand what would be causing this issue and the best way to fix it?
Thanks
When using Sort, the class of the parameters must implement IComparable interface.
public class Payments : IComparable<Payments>
{
public string Date { get; set; }
public string Payment { get; set; }
public string Reference { get; set; }
public decimal Amount { get; set; }
public int CompareTo(Payments otherPayment)
{
return DateTime.Parse(this.Date).ComapreTo(DateTime.Parse(otherPayment.Date));
}
}
Let's say I have a class from a 3rd-party, which is a data-model. It has perhaps 100 properties (some with public setters and getters, others with public getters but private setters). Let's call this class ContosoEmployeeModel
I want to facade this class with an interface (INavigationItem, which has Name and DBID properties) to allow it to be used in my application (it's a PowerShell provider, but that's not important right now). However, it also needs to be usable as a ContosoEmployeeModel.
My initial implementation looked like this:
public class ContosoEmployeeModel
{
// Note this class is not under my control. I'm supplied
// an instance of it that I have to work with.
public DateTime EmployeeDateOfBirth { get; set; }
// and 99 other properties.
}
public class FacadedEmployeeModel : ContosoEmployeeModel, INavigationItem
{
private ContosoEmployeeModel model;
public FacadedEmployeeModel(ContosoEmployeeModel model)
{
this.model = model;
}
// INavigationItem properties
string INavigationItem.Name { get; set;}
int INavigationItem.DBID { get; set;}
// ContosoEmployeeModel properties
public DateTime EmployeeDateOfBirth
{
get { return this.model.EmployeeDateOfBirth; }
set { this.model.EmployeeDateOfBirth = value; }
}
// And now write 99 more properties that look like this :-(
}
However, it's clear that this will involve writing a huge amount of boilerplate code to expose all the properties , and I'd rather avoid this if I can. I can T4 code-generate this code in a partial class, and will do if there aren't any better ideas, but I though I'd ask here to see if anyone had any better ideas using some super wizzy bit of C# magic
Please note - the API I use to obtain the ContosoEmployeeModel can only return a ContosoEmployeeModel - I can't extend it to return a FacededEmployeeModel, so wrapping the model is the only solution I can think of - I'm happy to be corrected though :)
The other approach may be suitable for you is to use AutoMapper to map base class to your facade here is sample code:
class Program
{
static void Main(string[] args)
{
var model = new Model { Count = 123, Date = DateTime.Now, Name = "Some name" };
Mapper.CreateMap<Model, FacadeForModel>();
var mappedObject = AutoMapper.Mapper.Map<FacadeForModel>(model);
Console.WriteLine(mappedObject);
Console.ReadLine();
}
class Model
{
public string Name { get; set; }
public DateTime Date { get; set; }
public int Count { get; set; }
}
interface INavigationItem
{
int Id { get; set; }
string OtherProp { get; set; }
}
class FacadeForModel : Model, INavigationItem
{
public int Id { get; set; }
public string OtherProp { get; set; }
}
}
Resharper allows the creation of "delegating members", which copies the interface of a contained object onto the containing object and tunnels the method calls/property access through to the contained object.
http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html
Once you've done that, you can then extract an interface on your proxy class.