I will plot an example to illustrate my behind the scene issues.
Let say I have this base generic class :
public abstract class ContainerBase<T>
{
Guid Id {get; init;}
IList<T> Items {get; set;}
bool IsLeaf {get; set;} = false;
/// omitted constructors and so
}
Then I have a whole bunch (undefined number) of concrete Container class that have another ContainerBase<...> as the T type argument :
public class RootContainer : ContainerBase<ChildContainer1>
{...}
public class ChildContainer1: ContainerBase<ChildContainer2>
{...}
public class ChildContainer2: ContainerBase<ChildContainer3>
{...}
...
public class ChildContainerNminus1: ContainerBase<ChildContainerN>
{...}
public class ChildContainerN: ContainerBase<int> // the recursion end here.
{
...
IsLeaf = true;
}
Now let say I have an AddContainer method from an Utility class and have access to the RootContainer object (a singleton for example) that is fully populated of recursive sub containers.
public static class ContainerUtility
{
// What is the Type of the recursive currentContainer ?
public static ContainerBase<T> FindContainer<T>(Guid id, ContainerBase<?> currentContainer)
{
if(currentContainer.Id == id)
return currentContainer;
if(currentContainer.IsLeaf) return default;
foreach(var item in currentContainer.Items)
{
var potential = FindContainer(id, item);
if(potential != default) return potential;
}
return default;
}
public static bool AddContainer<T>(ContainerBase<T> container, Guid parentId)
{
// potential should be of container's parent type (ContainerBase<"T-1">)
// but how to "bybass" an expected type parameter as I cannot know it ?
var potential = FindContainer<?>(parentId, RootContainer.Instance)
if(potential != default && potential is ContainerBase<?>)
{
potential.Items.Add(container)
return true;
}
return false;
}
}
You see, my issue is that I have a base type ContainerBase that is convenient for recursive search as all subClasses allow access to Items list to pursue recursion.
But at each step of the recursion it is a different actual type of ContainerBase<?>.
So I cannot perform cast on the method argument.
maybe use a top level interface that expose a List<object> Items ? Not sure that will end up good.
Bellow was my intermediate mid-solution on my issue.
I'll keep it for the record or erase it if you request it to clarify this response.
Ok I get something more interesting now. I would like to have your criticism of this solution I end up with :
Mainly I abstracted a way higher with a non generic interface to avoid my issue described in OP.
The Interface
public interface IContainer
{
int TAG { get; init; } // usefull for logging purpose
string Name { get; }
Guid Id { get; init; }
public bool IsLeaf { get;}
IList<IContainer>? GetContainers();
void SetContainers(List<IContainer> value);
}
The base class
public abstract class ContainerBase<T> : IContainer where T : IContainer
{
public int TAG { get; init; }
public Guid Id { get; init; }
private IList<IContainer>? _containers = new List<IContainer>();
public IList<T> Items { get; set; } = new List<T>();
public bool IsLeaf => this.GetType() == typeof(T);
public string Name => this.GetType().Name + "_" + TAG;
public ContainerBase(Guid id)
{
TAG = ContainerUtils.ContainerCount++;
Id=id;
}
public ContainerBase()
{
TAG = ContainerUtils.ContainerCount++;
Id = Guid.NewGuid();
}
public IList<IContainer>? GetContainers()
{
if(Items == null) return null;
if(_containers == null || !_containers.Any())
_containers = Items.Where(x => x!=null).Select(x => (IContainer)x!).ToList();
return _containers;
}
public void SetContainers(List<IContainer> value)
{
Items = new List<T>();
foreach(var item in value)
{
if (item is T)
Items.Add((T)item);
}
}
}
The concrete classes
internal class RootContainer : ContainerBase<Child1Container>
{
public RootContainer(Guid id) : base(id)
{
}
public RootContainer() : base()
{
}
}
The intermediate containers are the same only class name change (X = 1 to 3 in my test case)
internal class ChildXContainer : ContainerBase<ChildX+1Container>
{
public ChildXContainer(Guid id) : base(id)
{
}
public ChildXContainer() : base()
{
}
}
The leaf class (end point of my chained containers classes recursion).
internal class LeafContainer : ContainerBase<LeafContainer>
{
public int IntItem { get; set; }
public LeafContainer() : base()
{
}
public LeafContainer(Guid id) : base(id)
{
}
}
Do note I'm using a trick to detect if a ContainerBase<T> concrete implementation is a leaf or not :
If such classes are leaves then they have to derive from ContainerBase<> of themselves.
Kind like the CRTP syntax, but without its meaning.
So I'm not fully satisfied of this trick, but better than my previous attempt so far.
The Utility class
internal static class ContainerUtils
{
public static int ContainerCount = 0;
public static Guid IdToSearch {
get
{
if(!AllIds.Any())
return Guid.Empty;
return AllIds[new Random().Next(AllIds.Count - 1)];
}
//set { IdToSearch = value; }
}
public static List<Guid> AllIds { get; set; } = new();
private static RootContainer _root = BuildContainers();
public static RootContainer Root => _root;
private static RootContainer BuildContainers()
{
LeafContainer Leaf = new LeafContainer();
Child3Container Child3 = new Child3Container();
Child2Container Child2 = new Child2Container();
Child1Container Child1 = new Child1Container();
RootContainer Root = new RootContainer();
Root.Items.Add(Child1);
Child1.Items.Add(Child2);
Child2.Items.Add(Child3);
Child3.Items.Add(Leaf);
Leaf.IntItem = 12;
AllIds.Add(Root.Id);
AllIds.Add(Child1.Id);
AllIds.Add(Child2.Id);
AllIds.Add(Child3.Id);
AllIds.Add(Leaf.Id);
return Root;
}
private static IContainer? _GetSubContainer(this IContainer container, int index)
=> (container == null ||
container.GetContainers() == null ||
index >= container.GetContainers()!.Count) ? null : container.GetContainers()![index];
public static string ContainersToString()
=> ContainersToString(Root);
public static string ContainersToString(IContainer? fromContainer)
{
if (fromContainer == null) return string.Empty;
int i = 0;
string tab = " ";
string res = "";
while(fromContainer != null)
{
res += tab.Repeat(i) + "+" + fromContainer.Name??"NULL";
res += "\n";
i++;
fromContainer = _GetSubContainer(fromContainer, 0);
}
return res;
}
public static IContainer? SearchContainer(Guid id)
=> SearchContainer(id, Root);
public static IContainer? SearchContainer(Guid id, IContainer? fromContainer)
{
if (fromContainer == null) return null;
if (fromContainer.Id == id)
return fromContainer;
if (fromContainer.IsLeaf)
return null;
return SearchContainer(id, fromContainer._GetSubContainer(0));
}
public static bool SetItemToContainer(Guid id, IContainer newContainer)
{
var container = SearchContainer(id);
if(container == null) return false;
if (container._GetSubContainer(0) == null || (container.GetContainers()![0].GetType() != newContainer.GetType()))
return false;
container.GetContainers()![0] = newContainer;
return true;
}
}
The Program and its output
Console.WriteLine(ContainerUtils.ContainersToString());
IContainer newChild2 = new Child2Container();
Console.WriteLine("Child2's Name : " + ContainerUtils.SearchContainer(ContainerUtils.AllIds[2])?.Name ?? "NULL");
Console.WriteLine("New Child2's Name : " + newChild2.Name);
ContainerUtils.SetItemToContainer(ContainerUtils.AllIds[1], newChild2);
Console.WriteLine(ContainerUtils.ContainersToString());
Output
+RootContainer_4
+Child1Container_3
+Child2Container_2
+Child3Container_1
+LeafContainer_0
Child2's Name : Child2Container_2
New Child2's Name : Child2Container_5
+RootContainer_4
+Child1Container_3
+Child2Container_5
OLD answer
I created a new test project with a simpler version of my OP one.
Here is what I ended, and yep found using an interface as a "workish" solution (I'm not completely satisfied).
Let me know what you think of it please.
The interface without generic parameter :
public interface IContainer
{
string Name => this.GetType().Name;
public Guid Id { get; init; }
public IContainer? Item { get; set; }
public bool IsLeaf => Id == Guid.Empty;
}
The base abstract class with the generic parameter :
public abstract class ContainerBase<T> : IContainer where T : IContainer
{
public Guid Id { get; init; }
public T? Item { get; set; }
IContainer? IContainer.Item { get => Item; set => Item = (T)value; }
public ContainerBase(Guid id)
{
Id=id;
}
public ContainerBase()
{
Id = Guid.NewGuid();
}
}
The starting concrete Container class
internal class RootContainer : ContainerBase<Child1Container>
{
public RootContainer(Guid id) : base(id)
{
}
public RootContainer() : base()
{
}
}
The child Container concrete classes.
In my project there is Child1Container, Child2Container and Child3Container. I only display Child1Container here. The other are the same except class name.
internal class Child1Container : ContainerBase<Child2Container>
{
public Child1Container(Guid id) : base(id)
{
}
public Child1Container() : base()
{
}
}
The endind Container (noted Leaf here) :
This is where I found the code most ugly..
internal class LeafContainer : IContainer
{
public int IntItem { get; set; }
public Guid Id { get; init ; }
/// Meh, would be nice to avoid this.
public IContainer? Item { get => null; set => Item = null; }
public LeafContainer()
{
Id = Guid.Empty;
}
}
My utility class :
internal static class ContainerUtils
{
public static Guid IdToSearch { get; set; }
private static RootContainer _root = BuildContainers();
public static RootContainer Root => _root;
private static RootContainer BuildContainers()
{
LeafContainer Leaf = new LeafContainer();
Child3Container Child3 = new Child3Container();
Child2Container Child2 = new Child2Container();
Child1Container Child1 = new Child1Container();
RootContainer Root = new RootContainer();
Root.Item = Child1;
Child1.Item = Child2;
Child2.Item = Child3;
Child3.Item = Leaf;
Leaf.IntItem = 12;
IdToSearch = Root.Id;
return Root;
}
public static IContainer? SearchContainer(Guid id)
=> SearchContainer(id, Root);
public static IContainer? SearchContainer(Guid id, IContainer? fromContainer)
{
if (fromContainer == null) return null;
if(fromContainer.Id == id)
return fromContainer;
if(fromContainer.IsLeaf)
return null;
return SearchContainer(id, fromContainer.Item);
}
}
Finally my Program :
using TestRecursiveGenerics;
var res = ContainerUtils.SearchContainer(ContainerUtils.IdToSearch);
Console.WriteLine("Searching IContainer's Id, and we found : "+ res?.Name ?? "NULL");
Related
I have 3 interfaces.
public interface IItem
{
string Name { get; set; }
}
public interface IEquipable : IItem
{
void Equip();
}
public interface IConsumable : IItem
{
void Use();
}
IEquipable is implemented by the classes Helmet and Bow, and IConsumable is implemented by classes Potion and Food.
Then, I have a class with a property which contains a List of IItem, and proceed to add a few items of both IEquipable and IConsumable after instantiating it.
public class Character
{
public List<IItem> Items { get; private set; }
public Character()
{
this.Items = new List<IItem>();
}
public void AddItem(IItem item)
{
this.Items.Add(item);
}
}
Program.cs
...
Character char = new Character();
char.AddItem(new Potion());
char.AddItem(new Food());
char.AddItem(new Helmet());
char.AddItem(new Bow());
...
Is there a way I can get a List of all IEquipable members from the List of IItems, each AS IEquipable?
I want to do something like
...
List<IEquipable> equipmentList = //do something to char.Items and get all items of type IEquipable.
IEquipment equipment = equipmentList.First(...)
equipment.Equip();
...
I've tried using List<IEquipable> equipmentList = char.Items.OfType<IEquipable>().ToList() but the resulting list ends up empty.
I implemented (and fixed minor typos in) your code like this:
void Main()
{
Character character = new Character();
character.AddItem(new Potion());
character.AddItem(new Food());
character.AddItem(new Helmet());
character.AddItem(new Bow());
List<IEquipable> equipmentList = character.Items.OfType<IEquipable>().ToList();
}
public class Potion : IConsumable
{
public string Name { get; set; }
public void Use()
{
throw new NotImplementedException();
}
}
public class Food : IConsumable
{
public string Name { get; set; }
public void Use()
{
throw new NotImplementedException();
}
}
public class Helmet : IEquipable
{
public string Name { get; set; }
public void Equip()
{
throw new NotImplementedException();
}
}
public class Bow : IEquipable
{
public string Name { get; set; }
public void Equip()
{
throw new NotImplementedException();
}
}
public interface IItem
{
string Name { get; set; }
}
public interface IEquipable : IItem
{
void Equip();
}
public interface IConsumable : IItem
{
void Use();
}
public class Character
{
public List<IItem> Items { get; private set; }
public Character()
{
this.Items = new List<IItem>();
}
public void AddItem(IItem item)
{
this.Items.Add(item);
}
}
Your exact code (albeit char renamed to character) works perfectly fine. The equipmentList ends up with two elements. The issue you're seeing, i.e. "the resulting list ends up empty", is not reproducible with the code you've posted.
You can use the OfType method
Filters the elements of an IEnumerable based on a specified type.
Signature
public static IEnumerable<TResult> OfType<TResult> (this IEnumerable source)
Usage
var equipable = Character.Items.OfType<IEquipable>();
Or encapsulate it as a method in the instance or an extension method if you like
So it does work like I wanted. My actual code just had another issue and I'm a dummy for not actually posting that. So here it is, for future reference.
using System.Collections.Generic;
using RolePlayGame.Library.Items.Backstage;
using System.Linq;
using System.Text;
using System;
namespace RolePlayGame.Library.Characters.Backstage
{
public class Inventory
{
public List<IItem> StoredItems { get; private set; }
public List<EquippedItem> Gear { get; private set; }
public Inventory()
{
this.StoredItems = new List<IItem>();
this.Gear = new List<EquippedItem>();
}
public bool HasItem(string name)
{
return this.StoredItems.Exists(item => item.Name == name);
}
public bool HasItem(IItem item)
{
return this.StoredItems.Contains(item);
}
public void RemoveItem(string name)
{
int firstIndex = this.StoredItems.FindIndex(item => item.Name == name);
if (firstIndex != -1)
{
this.StoredItems.RemoveAt(firstIndex);
}
}
public void RemoveItem(IItem item)
{
int firstIndex = this.StoredItems.IndexOf(item);
if (firstIndex != -1)
{
this.StoredItems.RemoveAt(firstIndex);
}
}
public void AddItem(IItem item, int quantity)
{
for (int i = 0; i < quantity; i++)
{
this.StoredItems.Add(item);
}
}
public void AddItem(IItem item)
{
this.StoredItems.Add(item);
}
public bool CheckEquipmentSlot(EquipmentSlot slot)
{
return this.Gear.Exists(item => item.UsedSlots.Contains(slot));
}
public bool HasEquipment(IEquipment equipment)
{
return this.Gear.Exists(item => item.Item == equipment);
}
public void AddEquipment(IEquipment equipment)
{
IEquipment alreadyEquipped;
foreach (EquipmentSlot slot in equipment.SlotsUsed)
{
if (this.Gear.Exists(item => item.UsedSlots.Contains(slot)))
{
alreadyEquipped = this.Gear.Find(item => item.UsedSlots.Contains(slot)).Item;
this.RemoveEquipment(slot);
this.StoredItems.Add(alreadyEquipped);
}
}
EquippedItem newEquipment = new EquippedItem(equipment);
this.Gear.Add(newEquipment);
}
public void RemoveEquipment(EquipmentSlot slot)
{
this.Gear.RemoveAll(equipment => equipment.UsedSlots.Contains(slot));
}
public int GetAttributeBonusTotal(AttributeType attribute)
{
int bonusTotal = 0;
foreach (IEquipment item in this.StoredItems.OfType<IEquipment>().ToList())
{
bonusTotal += item.GetAttributeBonus(attribute);
}
return bonusTotal;
}
public int GetCarryWeight()
{
int totalWeight = 0;
foreach (IItem item in StoredItems)
{
totalWeight += item.Weight;
}
return totalWeight;
}
public string GearToString()
{
StringBuilder builder = new StringBuilder();
builder.Append(" Equipped Gear:");
foreach (EquippedItem equipment in this.Gear)
{
builder.Append($"\n {equipment.Item.Name}");
}
return builder.ToString();
}
public string ItemsToString()
{
StringBuilder builder = new StringBuilder();
builder.Append(" Inventory:");
foreach (IItem item in this.StoredItems.Distinct())
{
builder.Append($"\n {item.Name} x {this.StoredItems.FindAll(value => value == item).Count()}");
}
return builder.ToString();
}
public int GetDefenseRateAgainstTypeTotal(DamageType againstType)
{
int rate = 0;
List<IOutfit> outfits = this.Gear.Select(value => value.Item).OfType<IOutfit>().ToList();
foreach (IOutfit item in outfits)
{
rate += item.GetDefenseRateAgainstType(againstType);
}
return rate;
}
}
}
One of the last lines has the problem (now fixed). List<IOutfit> outfits = this.Gear.Select(value => value.Item).OfType<IOutfit>().ToList(); used to be List<IOutfit> outfits = this.Gear.OfType<IOutfit>().ToList();. But Gear is of type List<EquippedItem>, and EquippedItem is not an implementation of IItem.
Here is EquippedItem.cs
using RolePlayGame.Library.Items.Backstage;
using System.Collections.Generic;
namespace RolePlayGame.Library
{
public class EquippedItem
{
public List<EquipmentSlot> UsedSlots { get; set; }
public IEquipment Item { get; set; }
public EquippedItem(IEquipment equipment)
{
this.Item = equipment;
this.UsedSlots = equipment.SlotsUsed;
}
}
}
I needed to select the Item property from the items inside Gear as another list before doing the type filtering with .OfType<IOutfit>(). That's where .Select(value => value.Item) enters the stage.
So that's that. I'll learn to post actual code for future questions.
So playing with my own test Dependency Injector class. (yeah tons out there but this is just for fun)
Works decent but I don't know how to get the correct constructor based on the Interface passed in.
internal class DiContainer
{
private readonly Dictionary<Type, RegistryRecord> registry = new Dictionary<Type, RegistryRecord>();
private static DiContainer instance;
private DiContainer()
{
}
public static DiContainer GetInstance()
{
return instance ??= new DiContainer();
}
public void Register<T, C>() where C : class, T
{
registry.Add(typeof(T), new RegistryRecord
{
InterfaceType = typeof(T),
ConcreteType = typeof(C),
IsSingleTon = false
});
}
public void Register<C>() where C : class
{
Register(typeof(C));
}
public void Register(Type t)
{
registry.Add(t, new RegistryRecord
{
InterfaceType = t,
ConcreteType = t,
IsSingleTon = false
});
}
public void RegisterSingleton<T, C>(C instance = null) where C : class, T
{
registry.Add(typeof(T), new RegistryRecord
{
InterfaceType = typeof(T),
ConcreteType = typeof(C),
IsSingleTon = true,
Instance = instance
});
}
public T Get<T>()
{
return (T) Get(typeof(T));
}
public object Get(Type t)
{
ConstructorInfo constructor;
RegistryRecord r = null;
if (t.IsInterface && registry.ContainsKey(t))
{
r = registry[t];
if (r.IsSingleTon && r.Instance != null) return r.Instance;
constructor = r.ConcreteType.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
}
else
{
//todo how do we select the correct constructor?
constructor = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
}
var parameters = constructor.GetParameters();
//recurse to build dependency chain
var objects = parameters.Select(parameter => Get(parameter.ParameterType)).ToList();
var obj = constructor.Invoke(objects.ToArray());
if (r != null && r.IsSingleTon)
{
r.Instance = obj;
}
return obj;
}
}
internal class RegistryRecord
{
public Type InterfaceType { get; set; }
public Type ConcreteType { get; set; }
public object Instance { get; set; }
public bool IsSingleTon { get; set; }
}
So the problem is
constructor = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
I am just assuming the first constructor which is awful. But I have the definition of the interface I could be using.
How do I get the parameters of my interface and check them against the constructor?
Would like to select a constructor that matches the interface, or at least partially matches optimally.
edit
An example
using System;
namespace DITest
{
internal class Program
{
private static void Main(string[] args)
{
var di = DiContainer.GetInstance();
//Register classes / interfaces
di.Register<IPhoneResolver, PhoneResolver>();
di.Register<Customer>();
//Get class where dependency should be injected
var x = di.Get<Customer>();
Console.WriteLine(x.resolver.Name);
Console.Read();
}
}
public class Customer
{
//Remove this and everything is ok. Because we select the first one not the right one
public Customer()
{
}
public Customer(IPhoneResolver resolver)
{
this.resolver = resolver;
}
public IPhoneResolver resolver { get; set; }
}
public interface IPhoneResolver
{
string Name { get; set; }
bool DoesSomething();
}
public class PhoneResolver : IPhoneResolver
{
public string Name { get; set; } = "test";
public bool DoesSomething()
{
return true;
}
}
}
So because the first constructor is null there is an issue.
I need a way to resolve the correct constructor. I have the interface via the RegistryRecord and (type) InterfaceType. I need to find a way to get a constructor that matches that types parameters.
Environment:
I am working in Webapi. There is 2 entity classes which are follows;
public class Class1
{
public Class1()
{
this.items = new HashSet<Class2>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Class2> items { get; set; }
}
public class Class2
{
public int Id { get; set; }
public string Name { get; set; }
public int Class1Id { get; set; }
public virtual Class1 class1 { get; set; }
}
Business Layer:
The buniess layer have the following codes;
public class Class1Logic : IClass1Logic
{
private readonly IClass1Repository _repo;
public Class1Logic(IClass1Repository repository)
{
_repo = repository;
}
public async Task<bool> AddClass1ItemAsync(Class1 item)
{
_repo.Add(item);
bool status = await _repo.SaveAsync();
return status;
}
public async Task<Class1> GetClass1ItemAsync(int id)
{
return await _repo.GetAsync(id);
}
}
public class Class2Logic : IClass1Logic
{
private readonly IClass2Repository _repo;
public Class2Logic(IClass2Repository repository)
{
_repo = repository;
}
public async Task<bool> AddClass2ItemAsync(Class2 item)
{
_repo.Add(item);
bool status = await _repo.SaveAsync();
return status;
}
public async Task<Class2> GetClass2ItemAsync(int id)
{
return await _repo.GetAsync(id);
}
}
ViewModels:
public class Class1Model
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Class2Model
{
public int Id { get; internal set; }
public string Name { get; set; }
public int Class1Id { get; set; }
public string Class1Name { get; internal set; }
}
Controllers:
There are 2 contrtollers like Class1Controller and Class2Controller. Both have all CRUD operations.
[RoutePrefix("api/class1items")]
public class Class1Controller : ApiController
{
private readonly IClass1Logic _class1Logic;
private ModelFactory TheFactory;
public Class1Controller(IClass1Logic class1Logic)
{
_class1Logic = class1Logic;
TheFactory = new ModelFactory();
}
[Route("")]
public async Task<IHttpActionResult> Post(Class1Model class1Model)
{
var item = TheFactory.Parse(class1Model);
bool result = await _class1Logic.AddClassItemAsync(item);
if (!result)
{
return BadRequest("Error");
}
string uri = Url.Link("GetLabById", new { id = item.Id });
return Created(uri, TheFactory.Create(item));
}
[Route("{id:int}", Name = "GetClass1ItemById")]
public async Task<IHttpActionResult> GetClass1Item(int id)
{
Class1 item = await _class1Logic.GetClassItemAsync(id);
if (item == null)
{
return NotFound();
}
return Ok(TheFactory.Create(item));
}
}
[RoutePrefix("api/class2items")]
public class Class2Controller : ApiController
{
private readonly IClass2Logic _class2Logic;
private ModelFactory TheFactory;
public Class2Controller(IClass2Logic class2Logic)
{
_class2Logic = class2Logic;
TheFactory = new ModelFactory();
}
[Route("")]
public async Task<IHttpActionResult> Post(Class2Model class2Model)
{
var item = TheFactory.Parse(class2Model);
***//Here item should include Class1 object even if user give ClassId in class2Model***
bool result = await _class2Logic.AddClassItemAsync(item);
if (!result)
{
return BadRequest("Error");
}
string uri = Url.Link("GetClass2ItemById", new { id = item.Id });
return Created(uri, TheFactory.Create(item));
}
}
There is not dependecies in Class1. So all operations are fine. In Class2Controller post method, I got the model object as following to create Class2.
{
"id": 0,
"name": "string",
"class1Id": 1
}
Understanding:
I need to return this viewmodel to user after the create the record. The record created successfully but when mapping to viewmodel i got null exception as Class1 object not in the Class2 object.
In order to get the Class2 object including class1 object, I need to give the class1Object in the request object.
For this i need to find the Class1 object with Class1Id in the request object.
ViewMapper Code:
public class ModelFactory
{
public Class1Model Create(Class1 item)
{
return new Class1Model
{
Id = item.Id,
Name = item.Name
};
}
public Class2Model Create(Class2 item)
{
return new Class2Model
{
Id = item.Id,
Name = item.Name,
Class1Id = item.class1.Id,
Class1Name = item.class1.Name
};
}
public Class1 Parse(Class1Model modelItem)
{
return new Class1
{
Id = modelItem.Id,
Name = modelItem.Name
};
}
public Class2 Parse(Class2Model modelItem)
{
return new Class2
{
Id = modelItem.Id,
Name = modelItem.Name,
Class1Id = modelItem.Class1Id,
***/*Issue Place*/
//class1 = Need to set property by getting object using modelItem.Class1Id***
};
}
}
Issue:
Now i need to call get method of Class1Controller by passing Class1Id.
How to call and is this correct? or my design is bad?
This is initial case. If my Class3 have both Class1 and Class2 again i need to call methods of Class1 and Class2.
Please help to find the correct solution in this case
Note: I added comments the issue area to understand
Well, just to fix this issue you need to manually call _class1Logic.GetClass1ItemAsync after saving. However this doesn't look good.
More elegant ways to fix it:
1) If you always need Class2.Class1 field to be filled use Include when you fetch data (in repository): dbContext.Set<Class2>().Include(c => c.class1).
2) Also you can turn on LazyLoading for EF - I assume it should work in your case.
3) Inject class1Repo to class2Logic and fix up class1 reference after saving - in case if you don't want to enable lazy loading or item was detached from context after save method
Thoughts about design:
I suggest you to look at Automapper or simular libraries instead of ModelFactory where you going to have all mapping logic
Edit: About generic repository: you can modify you GetAsync method
public async Task<T> GetAsync<T>(int id, params Expression<Func<T, object>>[] includes)
where T: class, IEntity
{
var query = context.Set<T>().AsQueryable();
if (includes.Length > 0)
{
query = includes.Aggregate(query,
(current, include) => current.Include(include));
}
return await query.FirstOrDefaultAsync(x => x.Id == id);
}
IEntity interface:
interface IEntity
{
int Id { get; }
}
With this implementation you can use
await _repo.GetAsync<Class2>(id, x => x.class1);
I'm trying to control the depth of generation of an object tree with Autofixture. In some cases I want just to generate the root object and in another set of cases I may want to generate the tree up to a certain depth (2, 3, let's say).
class Foo {
public string Name {get;set;}
public Bar Bar {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
}
class Bar {
public string Name {get;set;}
public string Description {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
public Xpto Xpto {get;set;}
}
class Xpto {
public string Description {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
}
With the example above I would want (depth 1) to control the generation process so that only the Foo class is instantiated and the Bar property or any other reference type on that class is not populated or (depth 2) I would want the Foo class instantiated, the Bar property populated with a new instance of Bar but the Xpto property or any other reference type on that class not populated.
In case I did not spot it in the codebase does Autofixture have a customisation or behaviour to allow us to have that kind of control?
Again, it's not recursion that I want to control but the depth of population of the object graph.
No Bar
One-off:
var f = fixture.Build<Foo>().Without(f => f.Bar).Create();
Reusable:
fixture.Customize<Foo>(c => c.Without(f => f.Bar));
var f = fixture.Create<Foo>();
No Xpto
One-off:
var f = fixture
.Build<Foo>()
.With(
f => f.Bar,
fixture.Build<Bar>().Without(b => b.Xpto).Create())
.Create();
Reusable:
fixture.Customize<Bar>(c => c.Without(b => b.Xpto));
var f = fixture.Create<Foo>();
You can use the below GenerationDepthBehavior class as follows:
fixture.Behaviors.Add(new GenerationDepthBehavior(2));
public class GenerationDepthBehavior : ISpecimenBuilderTransformation
{
private const int DefaultGenerationDepth = 1;
private readonly int generationDepth;
public GenerationDepthBehavior() : this(DefaultGenerationDepth)
{
}
public GenerationDepthBehavior(int generationDepth)
{
if (generationDepth < 1)
throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");
this.generationDepth = generationDepth;
}
public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
return new GenerationDepthGuard(builder, new GenerationDepthHandler(), this.generationDepth);
}
}
public interface IGenerationDepthHandler
{
object HandleGenerationDepthLimitRequest(object request, IEnumerable<object> recordedRequests, int depth);
}
public class DepthSeededRequest : SeededRequest
{
public int Depth { get; }
public int MaxDepth { get; set; }
public bool ContinueSeed { get; }
public int GenerationLevel { get; private set; }
public DepthSeededRequest(object request, object seed, int depth) : base(request, seed)
{
Depth = depth;
Type innerRequest = request as Type;
if (innerRequest != null)
{
bool nullable = Nullable.GetUnderlyingType(innerRequest) != null;
ContinueSeed = nullable || innerRequest.IsGenericType;
if (ContinueSeed)
{
GenerationLevel = GetGenerationLevel(innerRequest);
}
}
}
private int GetGenerationLevel(Type innerRequest)
{
int level = 0;
if (Nullable.GetUnderlyingType(innerRequest) != null)
{
level = 1;
}
if (innerRequest.IsGenericType)
{
foreach (Type generic in innerRequest.GetGenericArguments())
{
level++;
level += GetGenerationLevel(generic);
}
}
return level;
}
}
public class GenerationDepthGuard : ISpecimenBuilderNode
{
private readonly ThreadLocal<Stack<DepthSeededRequest>> requestsByThread
= new ThreadLocal<Stack<DepthSeededRequest>>(() => new Stack<DepthSeededRequest>());
private Stack<DepthSeededRequest> GetMonitoredRequestsForCurrentThread() => this.requestsByThread.Value;
public GenerationDepthGuard(ISpecimenBuilder builder)
: this(builder, EqualityComparer<object>.Default)
{
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler)
: this(
builder,
depthHandler,
EqualityComparer<object>.Default,
1)
{
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler,
int generationDepth)
: this(
builder,
depthHandler,
EqualityComparer<object>.Default,
generationDepth)
{
}
public GenerationDepthGuard(ISpecimenBuilder builder, IEqualityComparer comparer)
{
this.Builder = builder ?? throw new ArgumentNullException(nameof(builder));
this.Comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
this.GenerationDepth = 1;
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler,
IEqualityComparer comparer)
: this(
builder,
depthHandler,
comparer,
1)
{
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler,
IEqualityComparer comparer,
int generationDepth)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (depthHandler == null) throw new ArgumentNullException(nameof(depthHandler));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
if (generationDepth < 1)
throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");
this.Builder = builder;
this.GenerationDepthHandler = depthHandler;
this.Comparer = comparer;
this.GenerationDepth = generationDepth;
}
public ISpecimenBuilder Builder { get; }
public IGenerationDepthHandler GenerationDepthHandler { get; }
public int GenerationDepth { get; }
public int CurrentDepth { get; }
public IEqualityComparer Comparer { get; }
protected IEnumerable RecordedRequests => this.GetMonitoredRequestsForCurrentThread();
public virtual object HandleGenerationDepthLimitRequest(object request, int currentDepth)
{
return this.GenerationDepthHandler.HandleGenerationDepthLimitRequest(
request,
this.GetMonitoredRequestsForCurrentThread(), currentDepth);
}
public object Create(object request, ISpecimenContext context)
{
if (request is SeededRequest)
{
int currentDepth = 0;
var requestsForCurrentThread = GetMonitoredRequestsForCurrentThread();
if (requestsForCurrentThread.Count > 0)
{
currentDepth = requestsForCurrentThread.Max(x => x.Depth) + 1;
}
DepthSeededRequest depthRequest = new DepthSeededRequest(((SeededRequest)request).Request, ((SeededRequest)request).Seed, currentDepth);
if (depthRequest.Depth >= GenerationDepth)
{
var parentRequest = requestsForCurrentThread.Peek();
depthRequest.MaxDepth = parentRequest.Depth + parentRequest.GenerationLevel;
if (!(parentRequest.ContinueSeed && currentDepth < depthRequest.MaxDepth))
{
return HandleGenerationDepthLimitRequest(request, depthRequest.Depth);
}
}
requestsForCurrentThread.Push(depthRequest);
try
{
return Builder.Create(request, context);
}
finally
{
requestsForCurrentThread.Pop();
}
}
else
{
return Builder.Create(request, context);
}
}
public virtual ISpecimenBuilderNode Compose(
IEnumerable<ISpecimenBuilder> builders)
{
var composedBuilder = ComposeIfMultiple(
builders);
return new GenerationDepthGuard(
composedBuilder,
this.GenerationDepthHandler,
this.Comparer,
this.GenerationDepth);
}
internal static ISpecimenBuilder ComposeIfMultiple(IEnumerable<ISpecimenBuilder> builders)
{
ISpecimenBuilder singleItem = null;
List<ISpecimenBuilder> multipleItems = null;
bool hasItems = false;
using (var enumerator = builders.GetEnumerator())
{
if (enumerator.MoveNext())
{
singleItem = enumerator.Current;
hasItems = true;
while (enumerator.MoveNext())
{
if (multipleItems == null)
{
multipleItems = new List<ISpecimenBuilder> { singleItem };
}
multipleItems.Add(enumerator.Current);
}
}
}
if (!hasItems)
{
return new CompositeSpecimenBuilder();
}
if (multipleItems == null)
{
return singleItem;
}
return new CompositeSpecimenBuilder(multipleItems);
}
public virtual IEnumerator<ISpecimenBuilder> GetEnumerator()
{
yield return this.Builder;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public class GenerationDepthHandler : IGenerationDepthHandler
{
public object HandleGenerationDepthLimitRequest(
object request,
IEnumerable<object> recordedRequests, int depth)
{
return new OmitSpecimen();
}
}
This feature was requested in a github issue. It was ultimately rejected. However, it was rejected because there was a nice, simple solution posted within the issue.
public class GenerationDepthBehavior: ISpecimenBuilderTransformation
{
public int Depth { get; }
public GenerationDepthBehavior(int depth)
{
Depth = depth;
}
public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
{
return new RecursionGuard(builder, new OmitOnRecursionHandler(), new IsSeededRequestComparer(), Depth);
}
private class IsSeededRequestComparer : IEqualityComparer
{
bool IEqualityComparer.Equals(object x, object y)
{
return x is SeededRequest && y is SeededRequest;
}
int IEqualityComparer.GetHashCode(object obj)
{
return obj is SeededRequest ? 0 : EqualityComparer<object>.Default.GetHashCode(obj);
}
}
}
You can then use this as follows:
fixture.Behaviors.Add(new GenerationDepthBehavior(2));
I want to use AbstractValidator<T> inside base entity class.
[Serializable]
public abstract class Entity<T> where T : Entity<T>
{
public virtual Boolean Validate(AbstractValidator<T> validator)
{
return validator.Validate(this as ValidationContext<T>).IsValid;
}
// other stuff..
}
But one of my tests fails saying that Validate() method couldn't accept null as a paramter.
[Test]
public void CategoryDescriptionIsEmpty()
{
var category = new Category
{
Title = "some title",
Description = String.Empty
};
Assert.False(category.Validate(this.validator) == true);
}
[SetUp]
public void Setup()
{
this.validator = new CategoryValidator();
}
I'm using Visual Web Developer and at the moment can't install C# Developer Express to create console application to debug the error. Since that I don't know how do I debug inside the unit test. Alternatively it would be great if some explanation could be given!
Thanks!
This topic is old, but I found useful and made a little diferent:
public abstract class WithValidation<V> where V : IValidator
{
private IValidator v = Activator.CreateInstance<V>();
public bool IsValid => !(Errors.Count() > 0);
public IEnumerable<string> Errors
{
get
{
var results = v.Validate(this);
List<string> err = new List<string>();
if (!results.IsValid)
foreach (var item in results.Errors)
err.Add(item.ErrorMessage);
return err;
}
}
}
public class Client : WithValidation<ClientValidator>
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class ClientValidator : AbstractValidator<Client>
{
public ClientValidator()
{
RuleFor(c => c.Name).NotNull();
RuleFor(c => c.Age).GreaterThan(10);
}
}
So you can use easier now like:
Client c = new Client();
var isvalid = c.IsValid;
IList<string> errors = c.Errors;
Ok!
So solution to my problem is next (at least this works as expected):
public abstract class Entity<T> where T : Entity<T>
{
public Boolean IsValid(IValidator<T> validator)
{
// var context = new ValidationContext(this);
// var instance = context.InstanceToValidate as T;
// return validator.Validate(instance).IsValid;
return validator.Validate(this as T).IsValid;
}
}
public class Rambo : Entity<Rambo>
{
public Int32 MadnessRatio { get; set; }
public Boolean CanHarmEverything { get; set; }
}
public class RamboValidator : AbstractValidator<Rambo>
{
public RamboValidator()
{
RuleFor(r => r.MadnessRatio).GreaterThan(100);
}
}
class Program
{
public static void Main(String[] args)
{
var ramboInstance = new Rambo {
MadnessRatio = 90
};
Console.WriteLine("Is Rembo still mad?");
Console.WriteLine(ramboInstance.IsValid(new RamboValidator()));
Console.ReadKey();
}
}