Let's say I have a warehouse with some "stuff" and I have some minions to run around this warehouse:
public class Warehouse {
public T GetItemInfo<T>(int id) where T : IItem, new() {}
public int AddItem<T>(T item) where T : IItem {}
public int RemoveItem<T>(int id) where T : IItem, new() {}
}
with minion interface:
public interface IMinion<T> where T : IItem {
T GetItemInfo(int id);
int AddItem<T>(T item);
int RemoveItem<T>(int id);
}
The PartMinion comes first:
public class PartMinion : IMinion<Part> {
protected Warehouse wh;
public PartMinion(Warehouse wh) {
this.wh = wh;
}
public Parts GetItemInfo(int id) {
return wh.GetItemInfo<Part>(id);
}
public int AddItem(int id) {
return wh.AddItem<Part>(id);
}
public int RemoveItem(int id) {
return wh.RemoveItem<Part>(id);
}
}
But, the FurnitureMinion is mostly the same as the PartMinion:
public class FurnitureMinion : IMinion<Furniture> {
protected Warehouse wh;
public FurnitureMinion(Warehouse wh) {
this.wh = wh;
}
public Furniture GetItemInfo(int id) {
return wh.GetItemInfo<Furniture>(id);
}
public int AddItem(int id) {
return wh.AddItem<Furniture>(id);
}
public int RemoveItem(int id) {
return wh.RemoveItem<Furniture>(id);
}
private IEnumerable<Parts> GetParts(int id) {
var query = "SELECT * FROM Parts " +
"JOIN FurnitureParts ON Parts.ID = FurnitureParts.PartID " +
"WHERE FurnitureParts.FurnitureID = ?";
var result = wh.Query<Parts>(query, id);
return result;
}
}
So I'd like to Abstract away the duplicate code between the two, but I can't quite get the syntax right:
public abstract class AbstractMinion : IMinion<T> {
protected Warehouse wh;
public AbstractMinion(Warehouse wh) {
this.wh = wh;
}
public Parts GetItemInfo(int id) {
return wh.GetItemInfo<T>(id);
}
public int AddItem(int id) {
return wh.AddItem<T>(id);
}
public int RemoveItem(int id) {
return wh.RemoveItem<T>(id);
}
}
Try adding a generic parameter to a class. You also need a where constraint, because this constraint exists on interface IMinion:
public abstract class AbstractMinion<T> : IMinion<T>
where T : IItem
{
protected Warehouse wh;
public AbstractMinion(Warehouse wh) {
this.wh = wh;
}
public T GetItemInfo(int id) {
return wh.GetItemInfo<T>(id);
}
public int AddItem(int id) {
return wh.AddItem<T>(id);
}
public int RemoveItem(int id) {
return wh.RemoveItem<T>(id);
}
}
So for PartMinion your class would look like this:
public class PartMinion : AbstractMinion<Part> {
public PartMinion(Warehouse wh) :base(wh){
}
}
Related
I am trying to implement a repository pattern, however I don't understand how its possible when entities have id's of different types. Currently I have to fallback to using object, while I would really like to use the specific type of the id of the entity the repository is holding.
interface IEntity
{
object GetId();
}
class Foo : IEntity
{
private string id;
public Foo(string id)
{
this.id = id;
}
public object GetId()
{
return id;
}
}
class Bar : IEntity
{
private int id;
public Bar(int id)
{
this.id = id;
}
public object GetId()
{
return id;
}
}
class Repository<T> where T : IEntity
{
private Dictionary<object, T> entities = new Dictionary<object, T>();
public IEnumerable<T> List => entities.Values.AsEnumerable();
public void Add(T entity)
{
entities.Add(entity.GetId(), entity);
}
public T Get(object id)
{
return entities[id];
}
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo("0");
var bar = new Bar(0);
var fooRepo = new Repository<Foo>();
fooRepo.Add(foo);
fooRepo.Get(foo.GetId());
var barRepo = new Repository<Bar>();
barRepo.Add(bar);
barRepo.Get(bar.GetId());
}
}
I also tried something like:
class Repository<Id, Value> where Value : IEntity
{
private Dictionary<Id, Value> entities = new Dictionary<Id, Value>();
public IEnumerable<Value> List => entities.Values.AsEnumerable();
public void Add(Value entity) // But got stuck here, I don't want to pass in Id as separate parameter, I want it auto magically from the interface.
{
entities.Add(entity.GetId(), entity);
}
public Value Get(Id id)
{
return entities[id];
}
}
You can add a generic type of the key to IEntity. E.g.,
interface IEntity<TId>
{
TId GetId();
}
class Foo : IEntity<string>
{
private string id;
public Foo(string id)
{
this.id = id;
}
public string GetId()
{
return id;
}
}
class Repository<TEntity, TId> where TEntity : IEntity<TId>
{
private Dictionary<TId, TEntity> entities = new Dictionary<TId, TEntity>();
public void Add(TEntity entity)
{
entities.Add(entity.GetId(), entity);
}
public TEntity Get(TId id)
{
return entities[id];
}
}
Joel's solution could be extended to allow for variance. The same way generic collections implement both IEnumerable and IEnumerable<T>.
Entity
interface IEntity
{
object GetId();
}
interface IEntity<TId> : IEntity
{
new TId GetId();
}
abstract class EntityBase<TId> : IEntity<TId>
{
protected TId id;
protected EntityBase(TId id)
{
this.id = id;
}
public TId GetId() => id;
object IEntity.GetId() => GetId();
}
Repository
abstract class Repository
{
protected Dictionary<object, IEntity> entities;
protected Repository()
{
entities = new Dictionary<object, IEntity>();
}
public virtual void Add(IEntity entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
entities.Add(entity.GetId(), entity);
}
public virtual IEntity Get(object id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
return entities[id];
}
}
abstract class Repository<TId, TEntity> : Repository
where TEntity : class, IEntity<TId>
{
protected Repository() : base() { }
public override void Add(IEntity entity)
{
Add((TEntity)entity);
}
public override IEntity Get(object id)
{
return Get((TId)id);
}
public void Add(TEntity entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
entities.Add(entity.GetId(), entity);
}
public TEntity Get(TId id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
return (TEntity)entities[id];
}
}
Use Case
class Foo : EntityBase<string>
{
public Foo(string id) : base(id) { }
}
class Bar : EntityBase<int>
{
public Bar(int id) : base(id) { }
}
class FooRepository : Repository<string, Foo>
{
public FooRepository() { }
}
class BarRepository : Repository<int, Bar>
{
public BarRepository() { }
}
Test
[TestMethod]
public void IEntitySupport()
{
// use IEntity and object
IEntity bar = new Bar(1);
Repository barRepository = new BarRepository();
barRepository.Add(bar);
var bar2 = barRepository.Get((object)1);
Assert.AreSame(bar, bar2);
}
[TestMethod]
public void TEntitySupport()
{
// use TEntity and TId
var foo = new Foo("a");
var fooRepository = new FooRepository();
fooRepository.Add(foo);
var foo2 = fooRepository.Get("a");
Assert.AreSame(foo, foo2);
}
I would like to be able to update a list by calling a method in my PersistentList instance.
public class PersistentList<T> : List<T> where T : class, new()
{
public void Update(T Item){//...}
}
so Instead of :
public ActionResult Edit(Article a)
{
if (ModelState.IsValid)
{
Article old = la.Find(p => p.Id == a.Id);
int index = pl.IndexOf(old);
pl[index] = a;
pl.SaveChanges();
return this.RedirectToAction("Index", "Home");
}
else { return View(); }
}
I want something like
public ActionResult Edit(Article a)
{
if (ModelState.IsValid)
{
pl.Update(a); //I'll call SaveChanges() in it.
return this.RedirectToAction("Index", "Home");
}
else { return View(); }
}
I'm a little lost. Any tips would be nice! Thank you
This is what I tried so far :
public void Update(T item)
{
T old = base.Find(p => p.GetHashCode() == item.GetHashCode());
int index = base.IndexOf(old);
base[index] = item;
SaveChanges();
}
Create an interface for your items to derive from containing the Id property:
public interface IItem
{
int Id { get; set; }
}
And have another generic constraint for your list on it:
public class PersistentList<T> : List<T> where T : class, new(), IItem
{
public void Update(T item)
{
// T must derive from the interface by constraint
T old = base.Find(p => p.Id == item.Id);
int index = base.IndexOf(old);
base[index] = item;
SaveChanges();
}
}
Is it possible that each class object has its own static data store?
I mean, just to perform actions like:
class Program
{
static void Main(string[] args)
{
var car1 = new Car();
car1.Save(); ////saves in its own storage
var own1 = new Owner();
own1.Save(); //saves in its own storage as well
}
}
In code I tried, I get such error
'System.InvalidCastException`
at this place
var repo = (Repository<IEntity>) CurrentRepository;
Whats wrong and how could I make it?
Whole code is here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//var car1 = new Car();
//car1.Save(); ////saves in its own storage
//var own1 = new Owner();
//own1.Save(); //saves in its own storage as well var car1 = new Car();
}
}
public interface IEntity
{
long Id { get; }
}
public class Owner : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Car Car { get; set; }
public Owner(string firstName, string lastName, Car car) : base(new Owner())
{
FirstName = firstName;
LastName = lastName;
Car = car;
}
public Owner() : base()
{
}
}
public class Car : Entity
{
public string Name { get; set; }
public int Year { get; set; }
public Car() : base()
{
}
public Car(string name, int year)
{
Name = name;
Year = year;
}
}
public abstract class Entity : IEntity
{
public long Id { get; }
public static object CurrentRepository { get; set; }
public Entity(Entity ent)
{
Type entityType = ent.GetType();
var instance = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(entityType));
CurrentRepository = instance;
}
public Entity()
{
}
public void Save()
{
var repo = (Repository<IEntity>)CurrentRepository;
repo.Save(this);
}
public void Delete()
{
var repo = (Repository<IEntity>)CurrentRepository;
repo.Delete(this);
}
}
public interface IRepository<T> where T : IEntity
{
void Save(T entity);
void Delete(T entity);
T Find(long id);
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
protected BaseStorage<T> CustomDataStorage;
public Repository()
{
CustomDataStorage = new BaseStorage<T>();
}
public void Save(T entity)
{
CustomDataStorage.Add(entity);
}
public void Delete(T entity)
{
CustomDataStorage.Remove(entity);
}
public T Find(long id)
{
throw new NotImplementedException();
}
}
public class BaseStorage<T> : IStorage<T>
{
List<T> data = new List<T>();
public void Add(T entity)
{
data.Add(entity);
}
public void Remove(T entity)
{
throw new NotImplementedException();
}
}
public interface IStorage<T>
{
void Add(T entity);
void Remove(T entity);
}
}
In code I tried, I get such error
'System.InvalidCastException`
at this place var repo = (Repository) CurrentRepository;
Whats wrong and how could I make it?
That's because you can't directly cast the CurrentRepository, a type of (Repository<Car> or Repository<Owner>) into a Repository<IEntity>.
See here for more information on this...
To achieve what you wanted here, you could try it this way:
Make the Entity class generic (Entity<T>) and constraint the generic type as IEntity
Make the CurrentRepository a type of Repository<Entity<T>>
Move the static initialization of CurrentRepository from instance constructor to the static constructor.
Make the Owner and Car object subclass of Entity<T> with T refers to its own type making it a self referencing generics
Code:
public class Owner : Entity<Owner>
{
...
}
public class Car : Entity<Car>
{
...
}
public abstract class Entity<T> : IEntity
where T: IEntity
{
public long Id { get; }
public static Repository<Entity<T>> CurrentRepository { get; set; }
static Entity()
{
CurrentRepository = new Repository<Entity<T>>();
}
public Entity()
{
}
public void Save()
{
CurrentRepository.Save(this);
}
public void Delete()
{
CurrentRepository.Delete(this);
}
}
One option to consider would be to change Entity as below.
The ConcurrentDictionary is to ensure that you get one repository per type.
public abstract class Entity : IEntity
{
public long Id { get; }
public static ConcurrentDictionary<Type, dynamic> CurrentRepository = new ConcurrentDictionary<Type, dynamic>();
public Entity(Entity ent)
{
GetRepository(ent);
}
private static dynamic GetRepository(Entity ent)
{
Type entityType = ent.GetType();
return CurrentRepository.GetOrAdd(entityType, type =>
{
var instance = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(entityType));
return instance;
});
}
public Entity()
{
}
public void Save()
{
var repo = GetRepository(this);
repo.Save((dynamic)this);
}
public void Delete()
{
var repo = GetRepository(this);
repo.Delete((dynamic)this);
}
}
I'm trying to create an abstract generic class which inherits from another abstract generic class.
Here's what I have so far
public abstract class BaseClass {
public long Id { get; private set; }
public BaseClass(long id) {
this.Id = id;
}
}
public abstract class BaseClass<T> : BaseClass where T : BaseClass {
protected BaseClass(long id)
: base(id) {
}
public static T Get(long id) {
T item;
return TryGet(id, out item) ? item : default(T);
}
public static bool TryGet(long id, out T item) {
item = null; // This is where I call the cache but for this example I've removed so it will compile
if (item != null) { return true; }
else {
// Call TryGetFallback method
return false;
}
}
protected abstract T TryGetFallback(long id);
}
public abstract class DerivedClass : BaseClass<DerivedClass> {
public String Name { get; private set; }
public DerivedClass(long id, String name)
: base(id) {
this.Name = name;
}
}
public class DerivedDerivedClass : DerivedClass {
protected override DerivedDerivedClass TryGetFallback(long id) {
// Handle the try get fallback
}
}
The TryGetFallback method on the DerivedDerivedClass causes a compiler error.
First you need to fix your BaseClass<T> implementation to not have a recursive type constraint.
public abstract class BaseClass<T> : BaseClass where T : new() {
//snip
}
Then you can use it in your derived class, for example I will make it use int for the generic type parameter:
public abstract class DerivedClass : BaseClass<int> {
//snip
}
And now if you compile it will warn you that 'DerivedDerivedClass' does not implement inherited abstract member 'BaseClass<int>.TryGetFallback(long)'
Thanks for the tips #DavidG it's helped me to solve the problem with the following code
public abstract class BaseClass {
public long Id { get; private set; }
public BaseClass(long id) {
this.Id = id;
}
}
public abstract class BaseClass<T> : BaseClass where T : BaseClass<T>, new() {
protected BaseClass(long id) : base(id) { }
public static T Get(long id) {
T item;
return TryGet(id, out item) ? item : default(T);
}
public static bool TryGet(long id, out T item) {
item = null; // Try to get item from cache here
if (item != null) { return true; }
else {
T obj = new T();
item = obj.TryGetFallback(id);
return item != null;
}
}
protected abstract T TryGetFallback(long id);
}
public abstract class DerivedClass<T> : BaseClass<T> where T : DerivedClass<T>, new() {
public String Name { get; private set; }
public DerivedClass() : base(0) { }
public DerivedClass(long id, String name)
: base(id) {
this.Name = name;
}
protected abstract override T TryGetFallback(long id);
}
public class DerivedDerivedClass : DerivedClass<DerivedDerivedClass> {
public DerivedDerivedClass() {
}
protected override DerivedDerivedClass TryGetFallback(long id) {
throw new NotImplementedException();
}
}
When having the following scenario I am unhappy with the consuming code that is littered with the line
var queryResult = _queryDispatcher.Dispatch<CustomerByIdQuery, CustomerByIdQueryResult>(customerByIdQuery).Customer;
I would prefer to have the code work this way for the consumer:
var queryResult = _queryDispatcher.Dispatch(customerByIdQuery).Customer;
Is there a way to accomplish this using generics?
Here is the code
interface IQuery{}
interface IQueryResult{}
interface IQueryHandler<TQuery, TQueryResult> : where TQueryResult:IQueryResult where TQuery:IQuery
{
TQueryResult Execute(TQuery query);
}
interface IQueryDispatcher
{
TQueryResult Dispatch<TQuery, TQueryResult>(TQuery query) where TQuery:IQuery where TQueryResult:IQueryResult
}
class GenericQueryDispatcher : IQueryDispatcher
{
public TQueryResult Dispatch<TQuery, TQueryResult>(TQuery parms)
{
var queryHandler = queryRegistry.FindQueryHandlerFor(TQuery);
queryHandler.Execute
}
}
class CustomerByIdQuery : IQuery
{
public int Id { get; set; }
}
class CustomerByIdQueryResult : IQueryResult
{
public Customer {get; set;}
}
class CustomerByIdQueryHandler : IQueryHandler
{
public CustomerByIdQueryResult Execute(TQuery query)
{
var customer = _customerRepo.GetById(query.Id);
return new CustomerByIdQueryResult(){Customer = customer};
}
}
public class SomeClassThatControlsWorkFlow
{
IQueryDispatcher _queryDispatcher;
public SomeClassThatControlsWorkFlow(IQueryDispatcher queryDispatcher)
{
_queryDispatcher = queryDispatcher;
}
public void Run()
{
var customerByIdQuery = new CustomerByIdQuery(){Id=1};
//want to change this line
var customer = _queryDispatcher.Dispatch<CustomerByIdQuery, CustomerByIdQueryResult>(customerByIdQuery).Customer;
}
}
Here is what I would like to have :
public class ClassWithRunMethodIWouldLikeToHave
{
IQueryDispatcher _queryDispatcher;
public SomeClassThatControlsWorkFlow(IQueryDispatcher queryDispatcher)
{
_queryDispatcher = queryDispatcher;
}
public void Run()
{
var customerByIdQuery = new CustomerByIdQuery(){Id=1};
//want to change this line
var customer = _queryDispatcher.Dispatch(customerByIdQuery).Customer;
}
}
Yes, it's possible, but you have to make the Dispatch method's parameter generic (that way, the compiler can infer the type parameters from the method parameter). To do this, it looks like you'll first need a generic version of the IQuery and IQueryResult interfaces:
interface IQuery<TQuery, TQueryResult> : IQuery {}
interface IQueryResult<T> : IQueryResult
{
T Result { get; }
}
Next, make CustomerByIdQuery and CustomerByIdQueryResult implement the respective generic interfaces:
class CustomerByIdQuery : IQuery, IQuery<int, Customer>
{
public int Id { get; set; }
}
class CustomerByIdQueryResult : IQueryResult, IQueryResult<Customer>
{
public Customer Result {get; set;}
}
Now you can add an overload for Dispatch that accepts the generic parameter:
interface IQueryDispatcher
{
IQueryResult<TQueryResult> Dispatch<TQuery, TQueryResult>(IQuery<TQuery, TQueryResult> parms);
}
class GenericQueryDispatcher : IQueryDispatcher
{
public TQueryResult Dispatch<TQuery, TQueryResult>(IQuery<TQuery, TQueryResult> parms)
{
// TODO implement
}
}
The above will allow you to write:
var customerByIdQuery = new CustomerByIdQuery{Id=1};
var customer = _queryDispatcher.Dispatch(customerByIdQuery).Result;
I can't get rid of the cast, but this is working pretty close to what I want.
public interface IQueryDispatcher
{
TQueryResult Dispatch<TParameter, TQueryResult>(IQuery<TQueryResult> query)
where TParameter : IQuery<TQueryResult>
where TQueryResult : IQueryResult;
}
public interface IQueryHandler<in TQuery, out TQueryResult>
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult
{
TQueryResult Retrieve(TQuery query);
}
public interface IQueryResult { }
public interface IQuery { }
public interface IQuery<TQueryResult> : IQuery { }
public class QueryDispatcher : IQueryDispatcher
{
readonly IQueryHandlerRegistry _queryRegistry;
public QueryDispatcher(IQueryHandlerRegistry queryRegistry)
{
_queryRegistry = queryRegistry;
}
public TQueryResult Dispatch<TQuery, TQueryResult>(IQuery<TQueryResult> query)
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult
{
var handler = _queryRegistry.FindQueryHandlerFor<TQuery, TQueryResult>(query);
//CANT GET RID OF CAST
return handler.Retrieve((TQuery)query);
}
}
public interface IQueryHandlerRegistry
{
IQueryHandler<TQuery, TQueryResult> FindQueryHandlerFor<TQuery, TQueryResult>(IQuery<TQueryResult> query)
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult;
}
public class GetCustByIdAndLocQuery : IQuery<CustByIdAndLocQueryResult>
{
public string CustName { get; set; }
public int LocationId { get; set; }
public GetCustByIdAndLocQuery(string name, int locationId)
{
CustName = name;
LocationId = locationId;
}
}
public class CustByIdAndLocQueryResult : IQueryResult
{
public Customer Customer { get; set; }
}
public class GetCustByIdAndLocQueryHandler : IQueryHandler<GetCustByIdAndLocQuery, CustByIdAndLocQueryResult>
{
readonly ICustomerGateway _customerGateway;
public GetCustByIdAndLocQueryHandler(ICustomerGateway customerGateway)
{
_customerGateway = customerGateway;
}
public CustByIdAndLocQueryResult Retrieve(GetCustByIdAndLocQuery query)
{
var customer = _customerGateway.GetAll()
.SingleOrDefault(x => x.LocationId == query.LocationId && x.CustomerName == query.CustName);
return new CustByIdAndLocQueryResult() { Customer = customer };
}
}
public interface ICustomerGateway
{
IEnumerable<Customer> GetAll();
}
public class Customer
{
public string CustomerName { get; set; }
public int LocationId { get; set; }
public bool HasInsurance { get; set; }
}