Model Relationships: efficiency in defining the relationship within the model - c#

A bit of a high-level question, more for the academics than the trench-diggers, I suppose.
Question 1:
When defining a foreign relationship with another model, say, a one-to-many relationship, it is typically defined in the following manner:
public virtual ICollection<OtherModel> OtherModel { get; set; }
However, I have also seen it defined in the following manner:
private ICollection<OtherModel> _otherModel;
public virtual ICollection<OtherModel> OtherModel {
get { return _otherModel ?? ( _otherModel = new List<OtherModel>() ); }
set { _otherModel = value }
}
This does make sense to me: if no entries of this model are referenced from the OtherModel (a null value), then the null-coalescing operator ensures that a blank, empty collection of OtherModel is created. From what I can tell, it’s a safety measure.
However, an evolution of the above appears to be this:
public class ThisModel {
// Assorted model items
public virtual ICollection<OtherModel> OtherModel { get; set; }
public ThisModel(){
OtherModel = new List<OtherModel>();
}
}
Unfortunately, I am not seeing how the two can be equivalent. The second code block above clearly uses the null-coalescing operator to call a blank list ONLY when OtherModel does not reference anything in ThisModel; when the resulting list would be null anyhow.
And when I read the third code block, I am interpreting it as a list of the OtherModel being created every single time ThisModel is called.
I was hoping someone could give me a bit of clarification on any differences between the two.
Question 2:
On the flip side of the coin, we have required entries in the OtherModel. Normally we build the reverse relationship in the OtherModel like this:
public virtual ThisModel ThisModel { get; set; }
However I have also seen it defined in the following manner:
public class OtherModel {
// Various model stuff
private ThisModel _thisModel;
public virtual ThisModel ThisModel {
get { return _thisModel; }
set {
if (value == null) throw new ArgumentNullException(nameof(value));
_thisModel= value;
ThisModelId = value.ThisModelId;
}
}
}
The key thing is, because OtherModel has a required foreign key, if that foreign key ends up being force-fed a null entry, the if statement explicitly throws a null exception. I like this. It ensures that for required foreign keys, a null value cannot be used or cannot be introduced. It ensures that any such rejection is done long before anything reaches the DB in a CRUD operation, and acts as a backup in case the business logic (higher up in the stack, with the View Models) was accidentally not extended to cover that issue.
My question in this case is how to condense this into something more efficient.

You are correct. They are different, and the constructor version is actually an anti-pattern. In the constructor, you're initializing an empty list whether or not the list has a value or not. For example, if EF were to initialize an instance where the list does have a value, first the value would be set to an empty list, then it would be set again to the list it should contain by EF. Granted, it's not that inefficient to simply create an empty list, but you are still consuming some amount of RAM and CPU for the operation that end up being unnecessary.
The custom getter and setter version is lazy-set, so an empty list is only initialized when the value is null, meaning no wasted resources. Again, it's not a huge deal, but a ton of little inefficiencies like this can eventually add up to real problems (like death by a thousand cuts).
Just to add a further wrinkle, though: in C# 6.0 you can actually provide a default without using a custom getter and setter, though. So the following is really the most optimal way:
public virtual ICollection<OtherModel> OtherModel { get; set; } = new List<OtherModel>();
It works exactly the same as the custom getter/setter version, just without all the cruft.

Related

Create and use covariant and mutable list (or potential workaround)

I'm currently modifying a Blazor library and the souce code of the current state is available on gitlab.
My situation is as follows:
I have a LineChartData object which is supposed to store multiple Datasets for LineCharts.
These Datasets intern have a List of Data. Instead of just working with List<object> I wanted to be able to have List<TData>.
Because there is a Mixed Chart which can accept both LineChartDatasets and BarChartDatasets, there is an interface called IMixableDataset.
I started by making this interface generic so it now looks like this (simplified):
public interface IMixableDataset<TData>
{
List<TData> Data { get; }
}
I then made my implementing class (LineChartDataset) generic as well and it now looks like this (simplified):
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public List<TData> Data { get; }
}
Next up was LineChartData. I first made this generic as well and continued with that until I reached the top level (see current state of my master branch). However I later wanted to change this because I wanted to support multiple Datasets with different kind of values. For this reason I reverted the generic stuff in all classes "above" the Datasets and the LineChartData now looks like this (simplified):
public class LineChartData
{
// HashSet to avoid duplicates
public HashSet<LineChartDataset<object>> Datasets { get; }
}
I decided to go with LineChartDataset<object> because: Since everything is castable to object, (in my mind) XYZ<Whatever> should also be castable to XYZ<object> but as I learned, this is not the case.
The where keyword didn't help either since I don't want to enforce TData to have relations apart from object - it could be int, string or something completely different. The only relation these LineDatasets are supposed to have is that they are LineDatasets, not what type they contain.
I then learned about Covariance and Contravariance (out and in-keyword). I tried out to make TData in IMixableDataset covariant but since List and IList/ICollection are all invariant I was unable to persue.
I also read about IReadOnlyCollection<> which is covariant but I cannot use this because I have to be able to modify the list after creation.
I have also tried using implicit/explicit operators to convert LineChartDataset<whatever> to LineChartDataset<object> but this has a few issues:
Since I created a new instance, I would need to store and use the new instance instead of the original one to add items, completely destroying the typesafety I had with the original one.
Since there are many more properties in LineChartDataset I would have to clone all of them as well.
If there is a way to convert a more specific one to the other while preserving the instance and not having to write code for every property this might be a solution.
Complete sample which reproduces the error I get and shows the issue:
// Provides access to some Data of a certain Type for multiple Charts
public interface IMixableDataset<TData>
{
List<TData> Data { get; }
}
// Contains Data of a certain Type (and more) for a Line-Chart
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public List<TData> Data { get; } = new List<TData>();
}
// Contains Datasets (and more) for a Line-Chart
// This class should not be generic since I don't want to restrict what values the Datasets have.
// I only want to ensure that each Dataset intern only has one type of data.
public class LineChartData
{
// HashSet to avoid duplicates and Public because it has to be serialized by JSON.Net
public HashSet<LineChartDataset<object>> Datasets { get; } = new HashSet<LineChartDataset<object>>();
}
// Contains the ChartData (with all the Datasets) and more
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
LineChartDataset<int> intDataset = new LineChartDataset<int>();
intDataset.Data.AddRange(new[] { 1, 2, 3, 4, 5 });
config.ChartData.Datasets.Add(intDataset);
// the above line yields following compiler error:
// cannot convert from 'Demo.LineChartDataset<int>' to 'Demo.LineChartDataset<object>'
// the config will then get serialized to json and used to invoke some javascript
}
public void WorkingButBadUseCase()
{
LineChartConfig config = new LineChartConfig();
LineChartDataset<object> intDataset = new LineChartDataset<object>();
// this allows mixed data which is exactly what I'm trying to prevent
intDataset.Data.AddRange(new object[] { 1, 2.9, 3, 4, 5, "oops there's a string" });
config.ChartData.Datasets.Add(intDataset); // <-- No compiler error
// the config will then get serialized to json and used to invoke some javascript
}
}
The reason everything only has getters is because of my initial attempt with using out. Even thought this didn't work out, I learned that you usually don't expose Setters for Collection-properties. This is not fix and also not very important for the question but I think worth mentioning.
Second complete example. Here I'm using out and an IReadOnlyCollection. I have removed the descriptions of the class (already visible in the previous example) to make it shorter.
public interface IMixableDataset<out TData>
{
IReadOnlyCollection<TData> Data { get; }
}
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public IReadOnlyCollection<TData> Data { get; } = new List<TData>();
}
public class LineChartData
{
public HashSet<IMixableDataset<object>> Datasets { get; } = new HashSet<IMixableDataset<object>>();
}
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
IMixableDataset<int> intDataset = new LineChartDataset<int>();
// since it's ReadOnly, I of course can't add anything so this yields a compiler error.
// For my use case, I do need to be able to add items to the list.
intDataset.Data.AddRange(new[] { 1, 2, 3, 4, 5 });
config.ChartData.Datasets.Add(intDataset);
// the above line yields following compiler error (which fairly surprised me because I thought I correctly used out):
// cannot convert from 'Demo.IMixableDataset<int>' to 'Demo.IMixableDataset<object>'
}
}
So the question:
Is there anyway to have a mutable and covariant collection?
If not, is there a workaround or something I can do to achieve this functionality?
Additional stuff:
I'm using the newest version of everything (.net core, VS, blazor, C#). Since the library is .NET Standard I'm still on C# 7.3 there.
In the repo under WebCore/Pages/FetchData you can perfectly see what I want to achieve (see comments at the end of the file).
Looking more closely at your example, I see one major problem: you are attempting to involve value types (e.g. int) in type variance. For better or worse, C# type variance applies only to reference types.
So, no…sorry, but it is quite impossible to do exactly what you're asking. You would have to represent all value-type based collections as object, not as their specific value types.
Now, as far as reference-type collections go, your example will work fine, with one minor change. Here's a modified version of your second example showing it working, with that one minor change:
public interface IMixableDataset<out TData>
{
IReadOnlyCollection<TData> Data { get; }
}
public class LineChartDataset<TData> : IMixableDataset<TData>
{
private readonly List<TData> _list = new List<TData>();
public IReadOnlyCollection<TData> Data => _list;
public void AddRange(IEnumerable<TData> collection) => _list.AddRange(collection);
}
public class LineChartData
{
public HashSet<IMixableDataset<object>> Datasets { get; } = new HashSet<IMixableDataset<object>>();
}
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
// Must use reference types to take advantage of type variance in C#
LineChartDataset<string> intDataset = new LineChartDataset<string>();
// Using the non-interface method to add the range, you can still mutate the object
intDataset.AddRange(new[] { "1", "2", "3", "4", "5" });
// Your original code works fine when reference types are used
config.ChartData.Datasets.Add(intDataset);
}
}
In particular, note that I've added an AddRange() method to your LineChartDataset<TData> class. This provides a type-safe way to mutate the collection. Note that the code that wants to mutate the collection must know the correct type, bypassing the variance restrictions.
The variant interface IMixableDataset<TData> itself cannot, of course, include a way to add things, because this would not be type-safe. You would be able to treat your LineChartDataset<string> as a IMixableDataset<object>, and then if you could add things via that interface, you'd be able to add some other type of object, even a non-reference type like a boxed int value, to your collection that's supposed to only contain string objects.
But, just as the invariant List<T> can implement the covariant IReadOnlyCollection<T>, your concrete LineChartDataset<TData> class can implement IMixableDataset<TData> while still providing a mechanism for adding items. This works because while the concrete type determines what the object can actually do, the interfaces simply define a contract that users of the reference must abide by, allowing the compiler to ensure type safety where the interface is used, even when used in a variant way. (The invariant concrete type ensures type safety as well, but only because the type has to match exactly, which is of course more restrictive/less flexible.)
If you don't mind using object in place of any specific value type for the value-type-based collections, then the above would work. It's a bit clumsy, since any time you actually want to get the value type values out, you'd need to retrieve them as object and then cast as necessary to actually use them. But at least the broader variant approach would then succeed, and no special handling would be required for any reference types.
Aside: that type variance in C# is restricted to reference types is based on the pragmatic requirement that type variance doesn't affect the runtime code. It's just a compile-time type-conversion. This means that you have to be able to just copy references around. To support value types would require adding new boxing and unboxing logic where it otherwise wouldn't exist. It's also not quite as useful, because value types don't have the same rich degree of type inheritance that reference types can have (value types can only ever inherit object, so variant scenarios are much less useful and interesting, in general).

Is it ok that 2 objects reference each other?

I'm making a chess game in C#. I've got 2 classes, Field and Piece:
public class Field
{
// the piece that is standing on this field
// null if no piece is standing on it
public Piece piece { get; set; }
}
public class Piece
{
// the field this piece is standing on
public Field field { get; set; }
}
When a piece moves, this method is called (in class Piece):
public void Move(Field field)
{
this.field = field;
field.piece = this;
}
This doesn't seem to be good coding, because everytime I change the field property, I also have to change the piece property for that field. I do need both properties though, because elsewhere in my code, I need them both to do checks etc (e.g. what's the field this piece is on and by what piece is this field taken).
My question: is this completely ok, is it a bad code smell or is it totally wrong? What would be a good solution to solve this?
Any advice? Thanks in advance!
The problem I see here is that you have Piece.field and Field.piece as public properties. This means that others can set these properties without updating the corresponding one.
Additionally, when you move a piece from one field to another, you don't remove the piece from the previous field, and we allow pieces to move to occupied squares, which will result in multiple pieces referring to the same field, but the field will only refer to the last piece placed there.
To address these, I would make the properties read only (with a private setter), forcing clients to call the corresponding Set or Move method to change them. Then, in this method, we can verify that the field we're moving to is not occupied (if it is, we simply throw an exception - the client must check this first before calling Move), and that we clear the Piece from the Field we moved from.
The validation work can be done in either the Field or Piece class, or both. I put it all in the Field class to simplify things.
Even still, there are problems with this. You can call Field.SetPiece(piece) directly (instead of Piece.MoveTo(field);), which will leave the piece with a null value for Field. So this is only a slight improvement, but not the ideal solution. See below for a better idea.
public class Field
{
public Piece Piece { get; private set; }
public bool Occupied => Piece != null;
public void ClearPiece()
{
// Remove this field from the piece
if (Piece?.Field == this) Piece.MoveTo(null);
// Remove the piece from this field
Piece = null;
}
public void SetPiece(Piece piece)
{
if (piece != null)
{
if (Occupied)
{
throw new InvalidOperationException(
$"Field is already occupied by {Piece}.");
}
// Remove piece from the piece's previous field
if (piece.Field?.Piece == piece)
{
piece.Field.ClearPiece();
}
}
Piece = piece;
}
}
public class Piece
{
public Field Field { get; private set; }
public void MoveTo(Field field)
{
field.SetPiece(this);
Field = field;
}
}
After thinking a little more about this, I think a better solution would be to have a GameManager class that handles all the validation and movement, and then we can make the Field and Piece classes "dumb".
This makes sense because there is a lot more validation to be done before setting a Piece on a Field. Is it ok to move this piece to the location (i.e. if the King is in check and this doesn't block it, then it's not allowed). Is the Field a valid landing spot for the piece based on the piece's move rules (i.e. a horizontal position for a bishop would not be allowed)? Is there anything blocking the path of the piece to get to the destination? Is the destination occupied by another piece belonging to the same player? Many things to evaluate before moving a piece.
Additionally, this would allow us to reuse the Piece and Field classes in other types of games, which may have a different set of rules, and a different GameManager to enforce them.
No! This relates to concept of circular dependency. Although applied for modules, this may very well be seen as precursor for such.
More concretely, this is an ideal example for mutually recursive objects. Conceptually, if you substitute (semi-pseudocode)
public class Field
{
public Piece piece {
public Field field {
public Piece piece {
...
}
}
}
}
That's because the objects are defined in terms of each other. Then theoretically you can
do something like
this.field.piece.field.piece...

Alternative to if/else on combinational logic in C#

I am working on a program that uses a grid system. The system needs a method that works on every element of the grid, based on the value of its neighbours' elementType. What I currently use is something along the lines of the follows:
enum ElementType{
A,B
}
if (neighbourUp.elemType == ElementType.A && neighbourDown == ElementType.A){
method1();
}
if (neighbourLeft == ElementType.A and current == ElementType.B){
method2();
}
and so on. As you can see this is hard to manage when the types increase. Ideally I would like to use polymorphism here but I feel creating a class for each combination is too much work. Also there may be cases where the method for some combinations is the same.
I would like some advice on how to approach this. Also the ElementType needs to be expandable to accommodate new types that may get added later.
You can use the Strategy Design Pattern which comes under Behavioral Patterns to avoid execution of code base on conditions
You can use these links to know more about strategy design patterns
https://www.dofactory.com/net/strategy-design-pattern
https://www.youtube.com/watch?v=v9ejT8FO-7I
store both of values in array with keys and apply for loop on neighbour and then ElementType, then you can compare using key values until the items in array.
I would define a list of rules and then your main code simply loops around all the cells and then invokes the rules that match for a cell. Then you can add or remove rules relatively easily in the future without changing the main loop logic.
So you need a class that represents a rule, each rule indicates the left/right/up/down types that it matches against and then has a method that is called if the match occurs. Make the matching fields nullable so you can indicate if you do not care what the neighbour type is...
class Rule
{
public ElementType? Left { get; set; }
public ElementType? Right { get; set; }
public ElementType? Top { get; set; }
public ElementType? Bottom { get; set; }
public Action Process { get; set; }
}
...in practice your 'Process' would need at least one parameter, a reference to the cell that is being processed. So you can change it to be Action or whatever.

A single DTO with multiple constructors - does this seem like an appropriate solution?

I have an entity called "Set" which contains Cards. Sometimes I want to see the entire card and its contents (card view), when sometimes I just want to know how many cards are in the Set (table views). In my effort to keep things DRY, I decided to try and re-use my SetDto class with multiple constructors like this:
public class SetDto
{
public SetDto()
{
Cards = new List<CardDto>();
}
// Called via SetDto(set, "thin")
public SetDto (Set set, string isThin)
{
var setDto = new SetDto()
{
SetId = set.SetId,
Title = set.Title,
Details = set.Details,
Stage = set.Stage,
CardCount = set.Cards.Count
};
return setDto;
}
// Called via SetDto(set)
public SetDto(Set set)
{
SetId = set.SetId;
UserId = set.UserId;
Title = set.Title;
Details = set.Details;
FolderId = set.FolderId;
Stage = set.Stage;
IsArchived = set.IsArchived;
Cards = new List<CardDto>();
foreach (Card card in set.Cards)
{
Cards.Add(new CardDto(card));
}
}
/// property definitions
I originally had two different DTOs for sets - ThinSetDto and FullSetDto - but this seemed messy and tougher to test. Does the above solution seem ok, or am I breaking a known best-practice? Thank you for your time!
I would create three methods in the SetManager class (a class handling CRUD operations) not in the DTO.
The dto shold have no such a logic inside. Anyway I agree with you that the replication is useless (and evil).
public class BaseSetDTO
{
public BaseSetDTO()
{
Set();
}
internal virtual void Set()
{
//Do your base set here with base properties
}
}
public class SetDTO : BaseSetDTO
{
internal override void Set()
{
//Do a full set here
}
}
Create a base class, then let your types handle what they are supposed to set. Create a new on for your ThinSetDTO and override again.
Instead, I would prefer extension method by declaring all properties in Set class and modifying the properties by passing required parameters. Otherwise initialize a baseDTO and have various versions by adding required properties and call extension method to create required version DTO and return baseDTO.
public static Set SetDto(this Set set, bool isThin)
{
if(isThin)
{
}
return objSet;
}
A common solution to this is to have the repository (or equivalent) return the 'flavor' of the DTO/entity you want by either having different access methods ie: Get() ... GetSet(), or to enumerate your 'flavors' of the entity in question and pass that to your 'Get' (or equivalent) method ie:
enum ContactCollectionFlavors { Full, CountOnly, CountWithNames .... }
...
foo = ContactRepository.GetByLastName('Jones', ContactCollectionFlavors.CountWithNames);
This can get a little messy, from experience the entity in question should have some way of knowing what 'flavor' it is, which smells bad since it breaks encapsulation and seperation of concerns - but in my opinion its better hold your nose and keep some out of band data, so that later you can have lazy loading of the entity allowing you to turn 'light flavors' into fully populated entities.

Uncertain about 'is' behavior with nHibernate Proxies

I have a situation where I need to determine whether or not an inherited class is a specific inherited class, but the type expected in the model is the base class, and it is stored using nHibernate/Fluent nHibernate using a Table Per Concrete Class Hierarchy. So my structure looks a bit like this..
class Mutation : Entity {
virtual Aspect Aspect { get; set; }
virtual Measurement Measurement { get; set; }
}
abstract class Measurement : Entity {
// does nothing on its own, really.
}
class Numeric : Measurement {
virtual int Value { get; set; }
// may have some other properties
}
class Observable : Measurement {
virtual Aspect Aspect { get; set; }
}
So basically, what is going on here is this. Mutation expects to be pointed to a type of data, and a Measured change (there could be other ways to change the data, not just flat numerics). So then I would have an object that merely expects an IList<Mutation> and map each subsequent type of Measurement to its own specific table, sharing the Identity with the base Measurement class. That works fine so far.
Now an Observable is different in that it does not store its own value, but rather it points again to another Aspect that may have its own set of mutations and changes. The idea is that the value will always be retrieved from the intended source, and not saved as a flat value in the database. (There is a reason for wanting this behavior that is beyond the scope of this question)
So then, my thought was basically to put in an evaluation like this ..
foreach(var measurement in list) {
if(measurement is Observable){
// then we know to lookup the other value
}
}
That didn't work. I still get the proxied result of just MeasurementProxy. But the same code works fine in a standalone C# application without the use of nHibernate, so I feel a great deal of confidence that the issue is with the proxy.
I then added the following method to my base Entity class...
/// <summary>
/// Unwrap the type from whatever proxy it may be
/// behind, returning the actual .NET <typeparamref name="System.Type"/>.
/// </summary>
/// <returns>
/// A pure <typeparamref name="System.Type"/> that is no longer proxied.
/// </returns>
public virtual Type Unwrap() {
return GetType();
}
Now if I do a Console.WriteLine(measurement.Unwrap()); I get the right type, but the same evaluation ...
foreach(var measurement in list) {
if(measurement.Unwrap() is Observable){
// then we know to lookup the other value
}
}
still does not function. It never runs. Can anyone help me out here?
That's because Unwrap() returns a Type, so measurement.Unwrap() is Observable will always be false, and measurement.Unwrap() is Type would always be true.
Use the typeof operator and reference equality instead:
if (measurement.Unwrap() == typeof(Observable)) {
// Then we know to lookup the other value.
}
The check in Hamidi answer is not enough. As soon as you add some lazy properties into your mapping, for example:
<property name="Description" type="StringClob" not-null="false" lazy="true"/>
the Unwrap method will fail, because for the types that employ lazy properties, the objects are always proxy, and the check
if (measurement.Unwrap() == typeof(Observable)) {
// Then we know to lookup the other value.
}
will fail because Unwrap will return the type of Proxy, not the expected type.
I use following methods for checking entity types:
public virtual Type GetEntityType()
{
var type = GetType();
// Hack to avoid problem with some types that always be proxy.
// Need re-evaluate when upgrade to NH 3.3.3
return type.Name.EndsWith("Proxy") ? type.BaseType : type;
}
public virtual bool IsOfType<T>()
{
return typeof(T).IsAssignableFrom(GetEntityType());
}
and the check becomes:
if (measurement.IsOfType<Observable>()) {
// Then we know to lookup the other value.
}
As you see in the code comment, this is a hack for NH 3.1 and Castle Proxy: Castle Dynamic Proxy types always end with Proxy, so I exploited this signature to detect if the object is proxy or not. My project is still stuck with NH3.1 so I'm not sure what changes the method will need with NH3.3.

Categories

Resources