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.
Related
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).
For the purposes of this question, a 'constant reference' is a reference to an object from which you cannot call methods that modify the object or modify it's properties.
I want something like this:
Const<User> user = provider.GetUser(); // Gets a constant reference to an "User" object
var name = user.GetName(); // Ok. Doesn't modify the object
user.SetName("New value"); // <- Error. Shouldn't be able to modify the object
Ideally, I would mark with a custom attribute (e.g. [Constant]) every method of a class that doesn't modify the instance, and only those methods can be called from the constant reference. Calls to other methods would result in an error, if possible, during compile time.
The idea is I can return a read-only reference to and be sure that it will not be modified by the client.
The technique you're referring to is called "const-correctness" which is a language feature of C++ and Swift, but not C#, unfortunately - however you're onto something by using a custom attribute because that way you can enforce it via a Roslyn extension - but that's a rabbit-hole.
Alternatively, there's a much simpler solution using interfaces: because C# (and I think the CLR too) does not support const-correctness (the closest we have is the readonly field modifier) the .NET base-class-library designers added "read-only interfaces" to common mutable types to allow a object (wheather mutable or immutable) to expose its functionality via an interface that only exposes immutable operations. Some examples include IReadOnlyList<T>, IReadOnlyCollection<T>, IReadOnlyDictionary<T> - while these are all enumerable types the technique is good for singular objects too.
This design has the advantage of working in any language that supports interfaces but not const-correctness.
For each type (class, struct, etc) in your project that needs to expose data without risk of being changed - or any immutable operations then create an immutable interface.
Modify your consuming code to use these interfaces instead of the concrete type.
Like so:
Supposing we have a mutable class User and a consuming service:
public class User
{
public String UserName { get; set; }
public Byte[] PasswordHash { get; set; }
public Byte[] PasswordSalt { get; set; }
public Boolean ValidatePassword(String inputPassword)
{
Hash[] inputHash = Crypto.GetHash( inputPassword, this.PasswordSalt );
return Crypto.CompareHashes( this.PasswordHash, inputHash );
}
public void ResetSalt()
{
this.PasswordSalt = Crypto.GetRandomBytes( 16 );
}
}
public static void DoReadOnlyStuffWithUser( User user )
{
...
}
public static void WriteStuffToUser( User user )
{
...
}
Then make an immutable interface:
public interface IReadOnlyUser
{
// Note that the interfaces' properties lack setters.
String UserName { get; }
IReadOnlyList<Byte> PasswordHash { get; }
IReadOnlyList<Byte> PasswordSalt { get; }
// ValidatePassword does not mutate state so it's exposed
Boolean ValidatePassword(String inputPassword);
// But ResetSalt is not exposed because it mutates instance state
}
Then modify your User class and consumers:
public class User : IReadOnlyUser
{
// (same as before, except need to expose IReadOnlyList<Byte> versions of array properties:
IReadOnlyList<Byte> IReadOnlyUser.PasswordHash => this.PasswordHash;
IReadOnlyList<Byte> IReadOnlyUser.PasswordSalt => this.PasswordSalt;
}
public static void DoReadOnlyStuffWithUser( IReadOnlyUser user )
{
...
}
// This method still uses `User` instead of `IReadOnlyUser` because it mutates the instance.
public static void WriteStuffToUser( User user )
{
...
}
So, these are the first two ideas I initially had, but don't quite solve the problem.
Using Dynamic Objects:
The first idea I had was creating a Dynamic Object that would intercept all member invokations and throw an error if the method being called isn't marked with a [Constant] custom attribute. This approach is problematic because a) We don't have the support of the compiler to check for errors in the code (i.e. method name typos) when dealing with dynamic objects, which might lead to a lot of runtime errors; and b) I intend to use this a lot, and searching for method names by name every time a method is called might have considerable performance impact.
Using RealProxy:
My second idea was using a RealProxy to wrap the real object and validate the methods being called, but this only works with objects that inherit from MarshalByRefObject.
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.
Imagine the following class used as a parameters object for some complex request.
public class RequestParameters
{
///<summary>Detailed User-Friendly Descriptions.</summary>
string Param1 { get; set; } = "Default";
int? Param2 { get; set; }
bool Param3 { get; set; } = true;
...
string ParamN { get; set; }
}
I want users to be able to create and configure this parameters object as they see fit before handing it off to be used in a request:
RequestParameters _filtered = new RequestParameters();
filtered.Param1 = "yellow";
filtered.Param3 = true;
_someInstance.InvokeRequest(_filtered);
Once I am processing the request, I wish to cache the result based on the request parameters supplied. It would be trivial to implement some clone method and IEquatable<RequestParameters>, and override Equals() and GetHashCode() on the above RequestParameters class, but the latter is widely regarded as bad practice - RequestParameters is mutable, and if an instance were modified after insertion, it could mess with my cache.
On the other hand, I feel like it would be a tremendous amount of code duplication (and a maintenance headache) if I were to implement some sort of ReadOnlyRequestParameters copy of this class, just without the setters.
Would it really be so bad to use this class as a cache key if I'm careful to make copies of the instances users send in, and never modify them once inserted into the cache? What alternatives would you propose if you were a developer working with the same objects?
In the spirit of MattE's suggestion, one potential solution (which I may end up using) is a compromise of the AsReadOnly() solution (which requires duplicating the class structure and a lot of copy/pasting).
If the primary goal of this class is user-friendly parameterization, and we're okay with its use as a cache key being a second-class citizen, a relatively simple way to implement it is by creating a GetHashKey() method. All this method needs to do is return an immutable object on which we can invoke "Equals" and "GetHashCode" - and guarantee that equivalent instances of this object will be treated equal. Tuples do this, but so do AnonymousTypes, which are leaner and easier to create for more than 6 parameters are involved:
/// <summary>Get a hashable / comparable key representing the current parameters.</summary>
/// <returns>The hash key.</returns>
public virtual object GetHashKey()
{
return new { Param1, Param2, Param3, Param4, ..., ParamN };
}
The returned AnonymousType correctly implements
GetHashCode() as a combined hash of each value in the object.
Equals() as a member-wise comparison of each value in the object.
Using this, we essentially produce a snapshot (copy) of the RequestParameters object in its current state. We sacrifice the ability to easily reflect on the properties, but make it easy and safe to use them as an Equatable/Hashable cache key without requiring any additional classes.
In my specific case, avoiding additional classes is particularly important as I don't just have 1 parameters class, I have dozens. What's more, many RequestParameter classes inherit from each other (as many requests share the same basic parameters). Creating a whole set of "ReadOnly" versions of these classes would be daunting. In this solution, each just has to override GetHashKey() to pair its base classes AnonymousType with its own additional parameters:
public class AdditionalParameters : RequestParameters
{
///<summary>Detailed User-Friendly Descriptions.</summary>
int? Param42 { get; set; };
...
string Param70 { get; set; }
public override object GetHashKey()
{
return new { base.GetHashKey(), Param42, ..., Param70 };
}
}
You could always store the parameters in a Tuple once you have gathered them as that is immutable once created....
Intro
I'm working with the legacy code which contains two classes:
I have a class which stores its value of System.Object type.
(I named this class as DomainItem)
Its Identifier property refers to
enum which holds information what a type of DomainItem is (in the
context of business domain).
There is also a class which stores these
items as an Enumerable List. (DomainItems)
What's more:
I don't want to change these classes into generic. This code is very sensitive and not covered by tests.
In order to get DomainItem, I must get it from DomainItems.Items collection.
Code
The code for classes is equivalent as below:
public class DomainItem
{
public Identifier Identifier { get; set; } // Readonly in the "real" code
public object Value { get; set; }
}
public class DomainItems
{
public IEnumerable<DomainItem> Items { get; set; }
}
The question is
How can I extend these classes using generics, to resolve type of Value property in the compile time. Is it even possible?
Example case might be as following:
DomainItem price = new DomainItem { Value = 25.20d, Identifier = Identifier.Price };
// ....
double priceValue = price.ProperValue; // generic property of type T
Obviously, above code is conceptual and it shows what I want to achieve. Any suggestions how to resolve that? Is it even possible?
Edit
My idea is to create a new IEnumerable<DomainItem<T>> where the collection is populated from non-generic DomainItem objects. Since the type of DomainItem.Value is known, it should be possible to make such collection somehow.
There's no such thing as a generic property, but you could easily create a generic method:
public T GetValue<T>() { ... }
public void SetValue<T>(T value) { ... }
You could then check typeof(T) within the method to make sure that it was appropriate for your identifier, ideally having made the identifier read-only. (It would be better as a constructor argument - I wouldn't expect it to make any sense to have a domain item whose identifier changed over time.)
Alternatively, you could just make the type of the Value property dynamic instead of object, assuming you're using C# 4+ with .NET 4+. Then your example code would compile - but it would perform an implicit (dynamic) conversion to double at execution time. You wouldn't get much safety there, but it would compile...