DateTime as class generic constraint - c#

Is there a way in C# to add a class constraint that simply makes sure that the type is either DateTime or Nullable<DateTime>? Or makes sure that they have an == operator that is compatible with DateTime?
Below is a simplified use case where I have a method which I try to re-use between two entitites. In reality it is reused between four different entitites. The all share a "similiar" interface but some have a DateTime? and others have DateTime for their Created-property.
I really just need to make sure that they can compare with a specific DateTime which is fetched by another method GetDateForLastMonth().
Limitations for this is that I cannot change the properties for the entities. Although, I can add new interfaces for them. I cannot change the returned type from GetDateForLastMonth.
public class Context : DbContext
{
public virtual DbSet<OrderEntity> Orders { get; set; }
public virtual DbSet<InvoiceEntity> Invoices { get; set; }
public IEnumerable<T> GetItemsForSomePeriod<T, TDateType>(DbSet<T> dbSet)
where T : class, IHaveACreationDate<TDateType>
where TDateType : DateTime // Does not compile because "Only class or interface could be specified as constraint"
{
DateTime someDate = GetSomeDate();
DateTime someOtherDate = GetSomeOtherDate();
DateTime someThirdDate = GetAThirdDate();
return dbSet
.Where(x => (x.Created == lastMonth || x.Created == someOtherDate || x.Create == someThirdDate)) // Cannot compare because Created and someDate isn't same Type
.ToArray();
}
}
public interface IHaveACreationDate<T>
{
T Created { get;}
}
public class OrderEntity: IHaveACreationDate<DateTime?>
{
public DateTime? Created { get; set; }
}
public class InvoiceEntity : IHaveACreationDate<DateTime>
{
public DateTime Created { get; set; }
}

Related

Is there a way to type check a generic function parameter using the shape of the object instead of inheritance?

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;
}

ASP.NET MVC: How can I put the StartDate of a current record on the EndDate of an earlier record

I have a class that makes appointments, the person making a appointments only inserts the start date and time, but the end date must be equal to the start date of the next appointments. My difficulty is in ensuring that the previous appointments always receives the EndDate as the StartDate of the current appointments
public class InfoAppointments : Entity
{
public bool Active { get; set; }
public bool Excluded { get; set; }
public string Status { get; set; }
public string Observation{ get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
EDIT
My Repository:
public class InfoAppointmentsRepository : Repository<InfoAppointments>, IAppointmentsRepository
{
public InfoAppointmentsRepository(RveContext rveContext) : base(rveContext)
{
}
public InfoAppointments FindByName(string name)
{
return Search(c => c.Name== name).FirstOrDefault();
}
public InfoAppointments FindByStatus()
{
throw new NotImplementedException();
}
public override void Remove(Guid id)
{
throw new NotImplementedException();
}
}
}
There are several possible solutions to this, and it may depend on your preference for adding this sort of business logic in your application code or in SQL (e.g. as a trigger). I would personally recommend the former, as this requirement may evolve over time, and may affect other components of your business logic.
I've made a few assumptions: 1) That you are using Entity Framework, and 2) Appointments don't overlap, and EndDate is unique. If that is the case, you could implement this functionality using similar logic to the following:
public class AppointmentService
{
private readonly MyContext _db;
public AppointmentService(MyContext db) => _db = db;
public void AddAppointment(InfoAppointments appointment)
{
// Update the previous appointment's end date
var previousAppointment = _db.Appointments
.OrderByDescending(e => e.EndDate)
.FirstOrDefault();
if (previousAppointment != null)
{
previousAppointment.EndDate = appointment.StartDate;
}
// Add the new appointment
_db.Appointments.Add(appointment);
_db.SaveChanges();
}
}
One other comment: based on your explanation, it appears that EndDate should default to null, but you've used a non-nullable type. I would change it to the following:
public DateTime? EndDate { get; set; }

How to check and set values of specific value object inside a generic class?

I Have a method in a repository class that sets insert metadata for a domain object:
private void SetInsertMetadata(TDomainEntity entity)
{
entity.InsertDT = DateTime.UtcNow;
}
I Need to check – does this entity class have a list of value objects as properties? If so, then set InsertDT in each of them.
How do I do this check from generic class?
Making some assumptions here. First that your interface looks like this:
public interface TDomainEntity
{
DateTime InsertDT { get; set; }
}
And you have a couple of entities:
public class EntityA : TDomainEntity
{
public EntityB BValue { get; set; }
public DateTime InsertDT { get; set; }
}
public class EntityB : TDomainEntity
{
public DateTime InsertDT { get; set; }
}
Your function to loop through every property and set the InsertDT property could be something like this:
private void SetInsertMetadata(TDomainEntity entity)
{
if(entity == null) return; //To prevent errors below
entity.InsertDT = DateTime.UtcNow;
//Get all properties of the entity that also implement the TDomainEntity interface
var props = entity.GetType().GetProperties()
.Where(p => typeof(TDomainEntity).IsAssignableFrom(p.PropertyType));
//Recurse through each property:
foreach(var p in props)
{
SetInsertMetadata((TDomainEntity)p.GetValue(entity));
}
}
Or you could merge those last lines together:
entity.GetType().GetProperties()
.Where(p => typeof(TDomainEntity).IsAssignableFrom(p.PropertyType))
.ToList()
.ForEach(p => SetInsertMetadata((TDomainEntity)p.GetValue(entity)));
If you also want to includeIEnumerable<TDomainEntity> properties, add this:
entity.GetType().GetProperties()
.Where(p => typeof(IEnumerable<TDomainEntity>).IsAssignableFrom(p.PropertyType))
.ToList()
.ForEach(p =>
{
var value = (IEnumerable<TDomainEntity>)p.GetValue(entity);
if(value == null) return;
value.ToList().ForEach(i => SetInsertMetadata((TDomainEntity)i));
});
Assuming your class definition looks a bit like:
public class GenericClass<TDomainEntity>
{
...
}
You want to specify that TDomainEntity inherits from an interface. Create an interface that looks like this:
public interface IDateTimeHolder
{
DateTime InsertTD { get; set; }
}
(Name the interface however you want)
Then update your generic class to specify the interface for the generic type:
public class GenericClass<TDomainEntity> where TDomainEntity : IDateTimeHolder
{
string Version { get; set; }
}
Also makes sure that the class passed in implements the interface.

Strategy for resolving correct interface implementation in multi-tenant environment

Given this interface:
public interface ILoanCalculator
{
decimal Amount { get; set; }
decimal TermYears { get; set; }
int TermMonths { get; set; }
decimal IntrestRatePerYear { get; set; }
DateTime StartDate { get; set; }
decimal MonthlyPayments { get; set; }
void Calculate();
}
and 2 implentations of it:
namespace MyCompany.Services.Business.Foo
{
public interface ILoanCalculator : Common.ILoanCalculator
{
}
public class LoanCalculator : ILoanCalculator
{
public decimal Amount { get; set; }
public decimal TermYears { get; set; }
public int TermMonths { get; set; }
public decimal IntrestRatePerYear { get; set; }
public DateTime StartDate { get; set; }
public decimal MonthlyPayments { get; set; }
public void Calculate()
{
throw new NotImplementedException();
}
}
}
namespace MyCompany.Services.Business.Bar
{
public interface ILoanCalculator : Common.ILoanCalculator
{
}
public class LoanCalculator : ILoanCalculator
{
public decimal Amount { get; set; }
public decimal TermYears { get; set; }
public int TermMonths { get; set; }
public decimal IntrestRatePerYear { get; set; }
public DateTime StartDate { get; set; }
public decimal MonthlyPayments { get; set; }
public void Calculate()
{
throw new NotImplementedException();
}
}
}
Given the simple code from above, lets say that the implementation of Calculate method will be different per company. What is the proper way to load the assemblies during initialization and call the correct method of the correct assembly? I have figured out the easy part with is determining which company the request is for, now I just need to call the correct method that corresponds to the current Business.
Thank you,
Stephen
Updated Example Code
Big shout out to #Scott, here are the changes I had to make in order for the accepted answer to work correctly.
In this case I had to use the Assembly Resolver to find my type. Note that I used an attribute to mark my assembly so that filtering based on it was simpler and less error prone.
public T GetInstance<T>(string typeName, object value) where T : class
{
// Get the customer name from the request items
var customer = Request.GetItem("customer") as string;
if (customer == null) throw new Exception("Customer has not been set");
// Create the typeof the object from the customer name and the type format
var assemblyQualifiedName = string.Format(typeName, customer);
var type = Type.GetType(
assemblyQualifiedName,
(name) =>
{
return AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetCustomAttributes(typeof(TypeMarkerAttribute), false).Any()).FirstOrDefault();
},
null,
true);
if (type == null) throw new Exception("Customer type not loaded");
// Create an instance of the type
var instance = Activator.CreateInstance(type) as T;
// Check the instance is valid
if (instance == default(T)) throw new Exception("Unable to create instance");
// Populate it with the values from the request
instance.PopulateWith(value);
// Return the instance
return instance;
}
Marker Attribute
[AttributeUsage(AttributeTargets.Assembly)]
public class TypeMarkerAttribute : Attribute { }
Usage in plugin assembly
[assembly: TypeMarker]
And finally, a slight change to the static MyTypes to support qualified name
public static class MyTypes
{
// assemblyQualifiedName
public static string LoanCalculator = "SomeName.BusinessLogic.{0}.LoanCalculator, SomeName.BusinessLogic.{0}, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
}
I don't think there is an easy or particularly elegant solution, because ServiceStack resolves it's services based on concrete classes rather than by interfaces, and this is something beyond the capability of Funq. However it's not impossible.
You will need to have a default implementation of each interface that you want to use as a DTO, because ServiceStack resolves using the concrete class.
So essentially here we have a DefaultCalculator which will provide us the route into our action method.
[Route("/Calculate","GET")]
public class DefaultCalculator : ILoanCalculator
{
public decimal Amount { get; set; }
public decimal TermYears { get; set; }
public int TermMonths { get; set; }
public decimal IntrestRatePerYear { get; set; }
public DateTime StartDate { get; set; }
public decimal MonthlyPayments { get; set; }
public void Calculate()
{
throw new NotImplementedException();
}
}
Then our action method is used almost as normal, except we call a method GetInstance<T> which we implement in our MyServiceBase from which this service extends, rather than Service, because it makes it easier to share this method across services.
public class TestService : MyServiceBase
{
public decimal Get(DefaultCalculator request)
{
// Get the instance of the calculator for the current customer
var calculator = GetInstance<ILoanCalculator>(MyTypes.LoanCalculator, request);
// Perform the action
calculator.Calculate();
// Return the result
return calculator.MonthlyPayments;
}
}
In MyServiceBase we implement the method GetInstance<T> which is responsible for resolving the correct instance, based on the customer name, of T, in this case is the ILoanCalculator.
The method works by:
Determine the customer name from the Request.GetItem("customer"). Your current method will need to set the customer identifier on the Request Items collections using Request.SetItem method at the point where you identify your customer. Or perhaps move the identification mechanism into this method.
With the customer name known the full type name can be built, based on the passed in type name template. i.e. MyCompany.Services.Business.Foo.LoanCalculator where Foo is the customer. This should resolve the type if the containing assembly has been loaded at startup.
Then an instance of the type is created as T i.e. the interface, ILoanCalculator
Then a safety check to make sure everything worked okay.
Then populate the values from the request that are in DefaultCalculator which is also of type ILoanCalculator.
Return the instance.
public class MyServiceBase : Service
{
public T GetInstance<T>(string typeName, object value)
{
// Get the customer name from the request items
var customer = Request.GetItem("customer") as string;
if(customer == null) throw new Exception("Customer has not been set");
// Create the typeof the object from the customer name and the type format
var type = Type.GetType(string.Format(typeName, customer));
// Create an instance of the type
var instance = Activator.CreateInstance(type) as T;
// Check the instance is valid
if(instance == default(T)) throw new Exception("Unable to create instance");
// Populate it with the values from the request
instance.PopulateWith(value);
// Return the instance
return instance;
}
}
You can optionally add a cache of instances to prevent having to use Activator.CreateInstance for each request.
If you are going to have many different types being created dynamically then you may wish to organise their type strings into a static class:
public static class MyTypes
{
public static string LoanCalculator = "MyCompany.Services.Business.{0}.LoanCalculator";
public static string AnotherType = "MyCompany.Services.Business.{0}.AnotherType";
//...//
}
Then all that's left to do is ensure that you add the assemblies with your different customer implementations are loaded into your application, which you could do from your AppHost Configure method.
foreach(var pluginFileName in Directory.GetFiles("Plugins", "*.dll"))
Assembly.Load(File.ReadAllBytes(pluginFileName));
Obviously this method relies on the full type name being of a specific format to match it to customers. There are other approaches but I believe this to be straightforward.
I hope that helps.

How to compare properties between two objects

I have two similar classes : Person , PersonDto
public class Person
{
public string Name { get; set; }
public long Serial { get; set; }
public DateTime Date1 { get; set; }
public DateTime? Date2 { get; set; }
}
&
public class PersonDto
{
public string Name { get; set; }
public long Serial { get; set; }
public DateTime Date1 { get; set; }
public DateTime? Date2 { get; set; }
}
I have two objects of both by equal values.
var person = new Person { Name = null , Serial = 123, Date1 = DateTime.Now.Date, Date2 = DateTime.Now.Date };
var dto = new PersonDto { Name = "AAA", Serial = 123, Date1 = DateTime.Now.Date, Date2 = DateTime.Now.Date };
I need to check value of all properties in two classes by reflection. My final goal is defined difference value of this properties.
IList diffProperties = new ArrayList();
foreach (var item in person.GetType().GetProperties())
{
if (item.GetValue(person, null) != dto.GetType().GetProperty(item.Name).GetValue(dto, null))
diffProperties.Add(item);
}
I did this, but result is not satisfactory. Count of diffProperties for result was 4but count of my expect was 1.
Of course all properties can have null values.
I need to a solution generic.
What must do I?
If you want to stick with comparison via reflection you should not use != (reference equality which will fail most of comparisons for boxed results of GetProperty calls) but instead use static Object.Equals method.
Sample how to use Equals method to compare two object in your reflection code.
if (!Object.Equals(
item.GetValue(person, null),
dto.GetType().GetProperty(item.Name).GetValue(dto, null)))
{
diffProperties.Add(item);
}
You may consider making the Person class implement the IComparable interface and implementing the CompareTo(Object obj) method.
Implement IEquatable interface
Looking at you classes not all values can be null you have a nullable long.
But that said.
I also made something like this and used this website for it. Just make it so that it can accept 2 different objects. I can't share my code because of licensing sorry.

Categories

Resources