Best Implementation to avoid if/else - c#

I am doing a program for vacation in a company and the time that is allowed to be in a specific holiday.
I used an Abstract class with an abstract method :
public abstract class Abstract : TimeLength
{
public AbstractTest(string employeeCode, string employee, string typeOfHoliday, DateTime startDate, DateTime endDate) : base(startDate, endDate, "")
{
TypeOfHoliday = typeOfHoliday;
Employee = employee;
EmployeeCode = employeeCode;
}
public string EmployeeCode { get; set; }
public string Employee { get; set; }
public string TypeOfHoliday { get; set; }
public abstract bool HolidayValidation(string typeOfHoliday);
}
And I used multiple class that inherent from this abstract class like this two :
class MarriageVacation : Abstract
{
public MarriageVacation(string employeeCode, string employee, string typeOfHoliday, DateTime startDate, DateTime endDate) : base(employeeCode, employee, typeOfHoliday, startDate, endDate)
{
}
public override bool HolidayValidation(string typeOfHoliday)
{
if (Days() > (int)Holiday.MarriageVacation)
{
MessageBox.Show("Marriage Vacation Can only be 5 Days");
return false;
}
else
return true;
}
}
class Bereavement : Abstract
{
public Bereavement(string employeeCode, string employee, string typeOfHoliday, DateTime startDate, DateTime endDate) : base(employeeCode, employee, typeOfHoliday, startDate, endDate)
{
}
public override bool HolidayValidation(string typeOfHoliday)
{
if (Days() > (int)Holiday.Bereavement)
{
MessageBox.Show("Bereavement Can only be 5 Days");
return false;
}
else
return true;
}
}
I use Enum for holidays
and in the main I want to register this based on the users choice like this :
List<Abstract> holiday = new List<Abstract>();
if(CmbTypeHolidays.Text == Holiday.Bereavement.ToString())
{
var holid = new Bereavement(CmbEmpHolidays.Text.Split('-')[0], CmbEmpHolidays.Text.Split('-')[1], CmbTypeHolidays.Text, Convert.ToDateTime(StartDateHolidays.Value), Convert.ToDateTime(EndDateHolidays.Value));
if (holid.HolidayValidation(CmbTypeHolidays.Text))
{
holiday.Add(holid);
var bindingList = new BindingList<Abstract>(holiday);
dataGridHolidays.DataSource = bindingList;
controlPanelHolidays.Visible = false;
}
}
else if (CmbTypeHolidays.Text == Holiday.MarriageVacation.ToString())
{
var holid = new MarriageVacation(CmbEmpHolidays.Text.Split('-')[0], CmbEmpHolidays.Text.Split('-')[1], CmbTypeHolidays.Text, Convert.ToDateTime(StartDateHolidays.Value), Convert.ToDateTime(EndDateHolidays.Value));
if (holid.HolidayValidation(CmbTypeHolidays.Text))
{
holiday.Add(holid);
var bindingList = new BindingList<Abstract>(holiday);
dataGridHolidays.DataSource = bindingList;
controlPanelHolidays.Visible = false;
}
}
I wanted to know a better way to implement this solution or just to change the code that inserts data to the abstract List

You will need to set up a factory that maps holiday type name to the class implementing it:
private class HolidayConstructorArgs {
public string EmployeeCode {get;set;}
public string Employee {get;set;}
public string TypeOfHoliday {get;set;}
public DateTime From {get;set;}
public DateTime To {get;set;}
}
private static readonly IDictionary<string,Func<HolidayConstructorArgs,AbstractHoliday>> HolidayByTypeCode =
new Dictionary<string,Func<HolidayConstructorArgs,AbstractHoliday>> {
[$"{Holiday.Bereavement}"] = a => new Bereavement(a.EmployeeCode, a.Employee, a.TypeOfHoliday, a.From, a.To)
, [$"{Holiday.MarriageVacation}"] = a => new MarriageVacation(a.EmployeeCode, a.Employee, a.TypeOfHoliday, a.From, a.To)
};
Now you can get the factory from the dictionary, and use it to instantiate the object:
if (HolidayByTypeCode.TryGetValue(CmbTypeHolidays.Text, out var factory)) {
// This is where the "magic" happens:
// Func<> will invoke the appropriate constructor without a conditional
var holid = factory(
new HolidayConstructorArgs {
EmployeeCode = CmbEmpHolidays.Text.Split('-')[0]
, Employee = CmbEmpHolidays.Text.Split('-')[1]
, TypeOfHoliday = CmbTypeHolidays.Text
, From = Convert.ToDateTime(StartDateHolidays.Value)
, To = Convert.ToDateTime(EndDateHolidays.Value)
}
);
// ... The rest of your code remains the same
}

I made this changes based on the answers on this questions this is the Main class (Abstract) :
public class AbstractTest : TimeLength
{
public AbstractTest(string employeeCode, string employee, Holiday typeOfHoliday, DateTime startDate, DateTime endDate) : base(startDate, endDate, "")
{
TypeOfHoliday = typeOfHoliday;
Employee = employee;
EmployeeCode = employeeCode;
}
public string EmployeeCode { get; set; }
public string Employee { get; set; }
public Holiday TypeOfHoliday { get; set; }
public bool HolidayValidation(Holiday typeOfHoliday)
{
return Days() > (int)typeOfHoliday;
}
}
And in the Main i changed into this :
Holiday MyStatus = (Holiday)Enum.Parse(typeof(Holiday), CmbTypeHolidays.Text, true);
var holid = new AbstractTest(CmbEmpHolidays.Text.Split('-')[0], CmbEmpHolidays.Text.Split('-')[1], MyStatus, Convert.ToDateTime(StartDateHolidays.Value), Convert.ToDateTime(EndDateHolidays.Value));
if (!holid.HolidayValidation(MyStatus))
{
holiday.Add(holid);
var bindingList = new BindingList<AbstractTest>(holiday);
dataGridHolidays.DataSource = bindingList;
controlPanelHolidays.Visible = false;
}
else
{
MessageBox.Show($"{holid.TypeOfHoliday} Cant be more than {(int)MyStatus} Days");
}
For the typeOfHoliday i used Holiday type so it is easier to work with and the choice that the user makes i convert it to Enum Holiday

You have the same(almost) implementation for HolidayValidation and you dont use typeOfHoliday.
From what you have posted you might as well add the Holiday enum as parameter and property to base class(Abstract) and not have any inheritance at all.
Implement the HolidayValidation in the base class and use the Holiday property to compare to Days

Related

Autofac: Custom delegate for single instance object exposed via interface

Consider the below code:
public interface IProduct { };
public class ConcreteProduct : IProduct
{
public ConcreteProduct(int id, DateTime manufacturedDate, DateTime expiryDate)
{
}
}
public delegate IProduct ProductFactory(int id, DateTime manufacturedDate, DateTime expiryDate);
I need to maintain a single instance for the IProduct, which is to be created by someone by calling the ProductFactory delegate, which will return a ConcreteProduct.
Please suggest how to achieve this with Autofac.
Please try below:
public delegate IProduct ProductFactory(int id, DateTime manufacturedDate, DateTime expiryDate);
public interface IProduct { };
public class ConcreteProduct : IProduct
{
public int ProductId { get; set; }
public DateTime ManufactureDt { get; set; }
public DateTime ExpiryDt { get; set; }
public ConcreteProduct(int id, DateTime manufacturedDate, DateTime expiryDate)
{
ProductId = id;
ManufactureDt = manufacturedDate;
ExpiryDt = expiryDate;
}
}
//Class where Factory is injected
public class ProductOrder
{
private readonly ProductFactory _prodFactory;
public ProductOrder(ProductFactory prodFactory)
{
_prodFactory = prodFactory;
}
public IProduct GenerateOrder()
{
return _prodFactory.Invoke(10, DateTime.Now, DateTime.Now.AddDays(4));
}
}
Autofac registrations:
static IContainer container;
private static void InitializeAutoFac()
{
var builder = new ContainerBuilder();
builder.Register<ProductFactory>(context =>
{
return (int id, DateTime dt, DateTime dt2) =>
{
IProduct prod = new ConcreteProduct(id, dt, dt2);
return prod;
};
}).SingleInstance();
builder.RegisterType<ProductOrder>().AsSelf();
container = builder.Build();
}
Usage:
static void Main()
{
InitializeAutoFac();
ProductOrder pOrder = container.Resolve<ProductOrder>();
IProduct prod = pOrder.GenerateOrder();
}
Hope it helps.

Changing 2 Function into one function

I have two functions that are similar. The only differences is that their using two different models.Is there a way to dynamically initialize the class that is going to be use.
ITagRepository tagRepo = new TagRepository();
ICategoryRepository catRepo = new CategoryRepository();
public void AddTagsDontExist(string tags)
{
var allTags = tagRepo.GetAllQueryAble();
string[] tag = tags.Split(',');
foreach (var item in tag)
{
if (allTags.Where(e => e.Name.Contains(item)).Count() == 0)
{
tagRepo.Add(new Tag
{
Name = item.ToString(),
DateAdded = DateTime.Now,
LastModifiedDate = DateTime.Now,
IsDeleted = false
});
}
}
}
public void AddCategoriesDontExist(string Categories)
{
var allCategory = catRepo.GetAllQueryAble();
string[] Category = Categories.Split(',');
foreach (var item in Category)
{
if (allCategory.Where(e => e.Name.Contains(item)).ToArray().Count() == 0)
{
catRepo.Add(new Category
{
Name = item.ToString(),
DateAdded = DateTime.Now,
LastModifiedDate = DateTime.Now,
IsDeleted = false
});
}
}
}
I would introduce a new interface which contains the methods you need to call, and make the other interfaces implement it. (Note that I've had to guess at the IQueryable stuff.)
interface IQueryable // My best guess at this. Substitute with the correct definition!
{
string Name { get; set; }
}
interface IRepository
{
IEnumerable<IQueryable> GetAllQueryAble();
void Add(string name, DateTime dateAdded, DateTime lastModifiedDate, bool isDeleted);
}
interface ITagRepository: IRepository
{
// ...
}
interface ICategoryRepository: IRepository
{
// ...
}
Then you can implement the method as follows (I've cleaned it up a bit):
public void AddItems(string items, IRepository repository)
{
var allTags = repository.GetAllQueryAble();
string[] tag = items.Split(',');
foreach (var item in tag)
{
if (!allTags.Any(e => e.Name.Contains(item)))
{
repository.Add
(
item,
DateTime.Now,
DateTime.Now,
false
);
}
}
}
Then if you can call that from the other methods like this:
public void AddTagsDontExist(string tags)
{
AddItems(tags, tagRepo);
}
public void AddCategoriesDontExist(string categories)
{
AddItems(categories, catRepo);
}
The implementation of the Add() methods would look like this (example shown only for ITagRepository implementation):
public sealed class TagRepository : IRepository
{
public string GetAllQueryAble()
{
return ""; // Replace with real implementation.
}
public void Add(string name, DateTime dateAdded, DateTime lastModifiedDate, bool isDeleted)
{
this.Add(new Tag(name, dateAdded, lastModifiedDate, isDeleted));
}
}
[EDIT]
Thinking about it, you may also need to add an interface for the items in the repository so that you can get at the .Name fields, but that would be a similar refactoring that uses the same approach as the other interfaces, so you should be able to extrapolate from that.
Here's an example, where I've invented a new IRepositoryItem interface. Note how the Tag class implements it:
interface IRepositoryItem
{
string Name { get; }
}
interface IRepository
{
IEnumerable<IRepositoryItem> GetAllQueryAble();
void Add(string name, DateTime dateAdded, DateTime lastModifiedDate, bool isDeleted);
}
interface ITagRepository: IRepository
{
// ...
}
interface ICategoryRepository: IRepository
{
// ...
}
public sealed class Tag: IRepositoryItem
{
public string Name
{
get
{
return "TODO: Implementation";
}
}
}
There might be such a possibility, you could use generic method that would accept type that has overlapping properties.
Here's example how it could look like
public interface ICategoryOrTag
{
string Name {get; set;}
DateTime DateAdded {get; set;}
DateTime LastModifiedDate {get; set;}
bool IsDeleted {get; set;}
}
public class Category : ICategoryOrTag
{
//Category specific
}
public class Tag : ICategoryOrTag
{
//Tag specific
}
public void AddEntityDontExist<T>(string entities) where T : ICategoryOrTag, new()
{
var allEntities = entityRepo<T>.GetAllQueryAble();
string[] entity = entities.Split(',');
foreach (var item in entity)
{
if (allEntities.Where(e => e.Name.Contains(item)).ToArray().Count() == 0)
{
entityRepo.Add(new T
{
Name = item.ToString(),
DateAdded = DateTime.Now,
LastModifiedDate = DateTime.Now,
IsDeleted = false
});
}
}
}

How can i get the time ( only hours)

I need to show the number of hours in a Grid, how ever when I use a method to get all of the columns, the results show the Date.
This is my scenario.
(My Entities)
public class Turno
{
public int Cod_Turno { get; set; }
public string Des_Turno { get; set; }
public string Des_NombreCorto { get; set; }
public DateTime Hor_Inicio { get; set; }
public DateTime Hor_Fin { get; set; }
public bool Flag_Activo { get; set; }
}
This is my Access Layer
public IList<Turno> Listar()
{
var turnos = new List<Turno>();
var comando = _baseDatos.GetStoredProcCommand("HOR_Listar_Turno");
using (var lector = _baseDatos.ExecuteReader(comando))
{
while (lector.Read())
{
turnos.Add(new Turno
{
Cod_Turno = lector.GetInt32(lector.GetOrdinal("Cod_Turno")),
Des_Turno = lector.GetString(lector.GetOrdinal("Des_Turno")),
Des_NombreCorto = lector.GetString(lector.GetOrdinal("Des_NombreCorto")),
Hor_Inicio = new DateTime(lector.IsDBNull(lector.GetOrdinal("Hor_Inicio")) ? 0 : lector.GetOrdinal("Hor_Inicio")),
Hor_Fin = new DateTime(lector.IsDBNull(lector.GetOrdinal("Hor_Fin")) ? 0 : lector.GetOrdinal("Hor_Fin")),
Flag_Activo = lector.GetBoolean(lector.GetOrdinal("Flag_Activo"))
});
}
}
comando.Dispose();
return turnos;
}
Is there a way to get a Time in the Line to get HOR_Inicio and HOR_Fin; I only need the hours and minutes.
If you want the hours and minutes as a string you could use DateTime.Now.ToString("HH:mm");

Sort array\list after object is changed

What is the best approach for sorting a generic list when one of its objects property is changed?
I have the following example to help explain what is needed.
public class Sending
{
public Sending(int id, DateTime dateSent)
{
this.Id = id;
this.DateSent = dateSent;
}
public int Id { get; set; }
public DateTime DateSent { get; set; }
}
public class Operation
{
public List<Sending> ItemsSent = new List<Sending>();
public Operation()
{
ItemsSent.Add(new Sending(1, new DateTime(2010, 6, 2)));
ItemsSent.Add(new Sending(2, new DateTime(2010, 6, 3)));
ItemsSent[1].DateSent = new DateTime(2010, 6, 1);
}
}
What is the best way to trigger a sort on the list to sort by date after the DateSent property is set? Or should I have a method to update the property and perform the sort?
You could implement IComparable<Sending> on Sending and call Sort() on the ItemsSent. I would suggest to write a method to update an object and update the list manually.
public class Sending: IComparable<Sending>
{
// ...
public int CompareTo(Sending other)
{
return other == null ? 1 : DateSent.CompareTo(other.DateSend);
}
}
What you can do is you first implement INotifyChanged.
Then do some thing like this;
public class Sending : INotifyChanged
{
private int id;
private DateTime dateSent;
public Sending(int id, DateTime dateSent)
{
this.Id = id;
this.DateSent = dateSent;
}
public int Id { get; set; }
public DateTime DateSent
{
get
{
return this.dateSend;
}
set
{
this.dateSent = value;
OnPropertyChangerd("DateSent");
//CallYou List<Sending> Sort method;
}
}
So whenever a new value will set the sort method will sort the list.

Can I tell the Table Name of an ActiveRecord class in c#?

I'm trying to verify if a schema matches the objects I'm initializing.
Is there a way to get the TableName of a class other than simply reflecting the class name?
I am using some class with explicit TableNames
Edit: using Joe's solution I added the case where you don't specify the table name, it could probably use a constraint
public string find_table_name(object obj)
{
object[] attribs = obj.GetType().GetCustomAttributes(typeof(Castle.ActiveRecord.ActiveRecordAttribute), false);
if (attribs != null)
{
ActiveRecordAttribute attrib = (Castle.ActiveRecord.ActiveRecordAttribute) attribs[0];
if (attrib.Table != null)
return attrib.Table;
return obj.GetType().Name;
}
return null;
}
If you have something like the following:
[ActiveRecord(Table = "NewsMaster")]
public class Article
{
[PrimaryKey(Generator = PrimaryKeyType.Identity)]
public int NewsId { get; set; }
[Property(Column = "NewsHeadline")]
public string Headline { get; set; }
[Property(Column = "EffectiveStartDate")]
public DateTime StartDate { get; set; }
[Property(Column = "EffectiveEndDate")]
public DateTime EndDate { get; set; }
[Property]
public string NewsBlurb { get; set; }
}
This will get you the table name:
[Test]
public void Can_get_table_name()
{
var attribs = typeof(Article).GetCustomAttributes(typeof(Castle.ActiveRecord.ActiveRecordAttribute), false);
if (attribs != null)
{
var attrib = (Castle.ActiveRecord.ActiveRecordAttribute) attribs[0];
Assert.AreEqual("NewsMaster", attrib.Table);
}
}
You could also use:
ActiveRecordModel.GetModel(typeof(Article)).ActiveRecordAtt.Table
see this testcase

Categories

Resources