While using SubSonic 3 with ActiveRecord T4 templates, the generated code shows many warnings about CLS-compliance, unused items, and lack of GetHashCode() implementation.
In order to avoid them, I did the following modifications:
// Structs.tt
[CLSCompliant(false)] // added
public class <#=tbl.CleanName#>Table: DatabaseTable
{ ...
// ActiveRecord.tt
[CLSCompliant(false)] // added
public partial class <#=tbl.ClassName#>: IActiveRecord
{
#region Built-in testing
#pragma warning disable 0169 // added
static IList<<#=tbl.ClassName#>> TestItems;
#pragma warning restore 0169 // added
...
public override Int32 GetHashCode() // added
{
return this.KeyValue().GetHashCode();
}
...
Is there a better way to get rid of the warnings? Or a better GetHashCode() implementation?
Currently, the only way to get rid of the warnings is to update your t4 templates and submit a bug/fix to Rob. Or wait until somebody else does.
As for the GetHashCode implementation, I don't think you're going to find a good way to do this through templates. Hash code generation is very dependent on what state your object contains. And people with lots of letters after their name work long and hard to come up with hash code algorithms that are fast and return results with low chances of collision. Doing this from within a template that may generate a class with millions of different permutations of the state it may hold is a tall order to fill.
Probably the best thing Rob could have done would be to provide a default implementation that calls out to a partial method, checks the result and returns it if found. Here's an example:
public partial class Foo
{
public override int GetHashCode()
{
int? result = null;
TryGetHashCode(ref result);
if (result.HasValue)
return result.Value;
return new Random().Next();
}
partial void TryGetHashCode(ref int? result);
}
public partial class Foo
{
partial void TryGetHashCode(ref int? result)
{
result = 5;
}
}
If you compile this without the implementation of TryGetHashCode, the compiler completely omits the call to TryGetHashCode and you go from the declaration of result to the check to see if it has value, which it never will, so the default implementation of the hash code is returned.
I wanted a quick solution for this as well. The version that I am using does generate GetHashCode for tables that have a primary key that is a single int.
As our simple tables use text as their primary keys this didn't work out of the box. So I made the following change to the template near line 273 in ActiveRecord.tt
<# if(tbl.PK.SysType=="int"){#>
public override int GetHashCode() {
return this.<#=tbl.PK.CleanName #>;
}
<# }#>
<# else{#>
public override int GetHashCode() {
throw new NotImplementedException();
}
<# }#>
This way GetHashCode is generated for all the tables and stops the warnings, but will throw an exception if called (which we aren't).
We use this is for a testing application, not a website or anything like that, and this approach may not be valid for many situations.
Related
I have been using C#/.NET/Visual Studio since 2000, but I just started working with Xamarin Forms recently. It's a pretty great platform, however I have run across a couple of issues that I cannot figure out. (I'm using Visual Studio 2017 Community). I have been researching this behavior extensively, but have had absolutely no luck in obtaining a resolution. I have created a small application which easily reproduces both of these behaviors and would greatly appreciate any expertise that would help me fix my code so it works as expected. The two issues are these:
1) After creating a custom object using new in Xamarin Forms, the object is not null. However, if that object is passed via the parameter list into a static method, it becomes null inside the method. (Note: In my sample applications I placed the static method inside the Form1 and MainPage classes. I did this only to simplify the samples. The behavior is the same when I put the static method into a separate utility class, either static class or normal class.)
2) Custom enumerations are treated as integers instead of the actual enumerated type. For example, if an instance of an enumerated type is given a value of the first item in the enumeration, its value is 0, an integer. Whereas in a Windows application, the value would be the actual enumeration value (e.g., EnumValue1), and its type would be the enumerated type. This can be a problem if one is trying to do comparisons with strings. For example, aEnum.ToString() == "EnumValue1" will return true in a Windows application, but will return false in Xamarin Forms, because aEnum.ToString() will evaluate to "0". This behavior also breaks the tenet of encapsulation in object-oriented programming, in that you aren't supposed to have to know the underlying implementation of a data type in order to properly use it.
Below is the code that reproduces the issues. There are 2 separate projects, one Windows Forms, the other Xamarin Forms. (The Xamarin Forms project was created with .NET Standard code sharing strategy). Both projects use 2 utility classes Enumerations and CustObj, where the only differences are the namespace definitions (namespace WinFormApp.Utilities and namespace XamarinFormsApp.Utilities, respectively). They both reside in a Utilities folder under their respective projects.
Windows Forms "Form1.cs"
using System.Windows.Forms;
using WinFormApp.Utilities;
namespace WinFormApp
{
partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// ENUMERATION TEST
ACustomEnumeration aCE = ACustomEnumeration.EnumValue1;
string aceStr = aCE.ToString();
// PASSING OBJECT TO STATIC METHOD
CustObj aCustObj = new CustObj();
int ii = aCustObj.i;
string ss = aCustObj.s;
Form1.Foo(aCustObj);
}
public static void Foo(CustObj custObj)
{
}
}
}
Xamarin Forms "MainPage.xaml.cs"
using Xamarin.Forms;
using XamarinFormsApp.Utilities;
namespace XamarinFormsApp
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
// ENUMERATION TEST
ACustomEnumeration aCE = ACustomEnumeration.EnumValue1;
string aceStr = aCE.ToString();
// PASSING OBJECT TO STATIC METHOD
CustObj aCustObj = new CustObj();
int ii = aCustObj.i;
string ss = aCustObj.s;
MainPage.Foo(aCustObj);
}
public static void Foo(CustObj custObj)
{
}
}
}
Utility class "Enumerations.cs"
namespace WinFormApp.Utilities (or XamarinFormsApp.Utilities)
{
public enum ACustomEnumeration
{
EnumValue1,
EnumValue2
}
class Enumerations
{
}
}
Utility class "CustObj.cs"
namespace WinFormApp.Utilities (or XamarinFormsApp.Utilities)
{
public class CustObj
{
public ACustomEnumeration _aCEMember;
public int i;
public string s;
public CustObj()
{
this.i = 99;
this.s = "Hello!";
}
}
}
When stepping through the WinFormApp in Visual Studio, one observes the following variables before the call to static method Foo:
aCE with value of EnumValue1 and Type ACustomEnumeration
aceStr with Value of "EnumValue1" and Type string
aCustObj with Type CustObj and data members:
_aCEMember with Value of EnumValue1 and Type ACustomEnumeration
i with Value of 99 and Type int
s with Value of "Hello!" and Type string
ii with Value of 99
ss with Value of Hello!
After stepping into Foo, the parameter is as expected:
custObj with Type CustObj and data members:
_aCEMember with Value of EnumValue1 and Type ACustomEnumeration
i with Value of 99 and Type int
s with Value of "Hello!" and Type string
All variables evaluate as expected. Enumerations evaluate as the actual enumerated value. The custom object is instantiated and its data members are given their appropriate initial values. When the object is passed into the static method, it is passed correctly as a reference type, and retains all of its data inside the method.
Conversely, when stepping through the XamarinFormsApp in Visual Studio, one observes the following variables before the call to static method Foo:
aCE with Value of 0 and Type int (violates encapsulation)
aceStr with Value of "0" and Type string (this demonstrates the problem with how enumerations are evaluated)
aCustObj with Type CustObj and data members:
_aCEMember with no Value or Type at all
i with no Value or Type at all
s with no Value or Type at all
ii with Value of 99
ss with Value of Hello!
After stepping into Foo, the parameter is NOT as expected:
custObj with Value of null and Type CustObj
The problems with enumerations are fairly obvious. But I am very confused as to what is really going on with the custom object. It makes no sense that when I examine an object after its instantiation, that it doesn't show its data members as being initialized. Although it is strange how variables ii and ss are actually given the correct values.
More importantly, the fact that when I pass the object into the static method, it becomes null, is really mind-boggling.
Does anyone have any ideas as to what is happenning, and how to fix it?
I was reading a book about Linq, and saw that the Distinct method has an overload that takes a comparer. This would be a good solution to a problem I have where I want to get the distinct entities from a collection, but want the comparison to be on the entity ID, even if the other properties are different.
According to the book, if I have a Gribulator entity, I should be able to create a comparer like this...
private class GribulatorComparer : IComparer<Gribulator> {
public int Compare(Gribulator g1, Gribulator g2) {
return g1.ID.CompareTo(g2.ID);
}
}
...and then use it like this...
List<Gribulator> distinctGribulators
= myGribulators.Distinct(new GribulatorComparer()).ToList();
However, this gives the following compiler errors...
'System.Collections.Generic.List' does not contain a definition for 'Distinct' and the best extension method overload 'System.Linq.Enumerable.Distinct(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEqualityComparer)' has some invalid arguments
Argument 2: cannot convert from 'LinqPlayground.Program.GribulatorComparer' to 'System.Collections.Generic.IEqualityComparer'
I've searched around a bit, and have seen plenty of examples that use code like this, but no complaints about compiler errors.
What am I doing wrong? Also, is this the best way of doing this? I want a one-off solution here, so don't want to start changing the code for the entity itself. I want the entity to remain as normal, but just in this one place, compare by ID only.
Thanks for any help.
You're implementing your comparer as an IComparer<T>, the LINQ method overload requires an implementation of IEqualityComparer:
private class GribulatorComparer : IEqualityComparer<Gribulator> {
public bool Equals(Gribulator g1, Gribulator g2) {
return g1.ID == g2.ID;
}
}
edit:
For clarification, the IComparer interface can be used for sorting, as that's basically what the Compare() method does.
Like this:
items.OrderBy(x => new ItemComparer());
private class ItemComparer : IComparer<Item>
{
public int Compare(Item x, Item y)
{
return x.Id.CompareTo(y.Id)
}
}
Which will sort your collection using that comparer, however LINQ provides a way to do that for simple fields (like an int Id).
items.OrderBy(x => x.Id);
I'd like to create an IList<Child> that maintains its Child objects in a default/implicit sort order at all times (i.e. regardless of additions/removals to the underlying list).
What I'm specifically trying to avoid is the need for all consumers of said IList<Child> to explicitly invoke IEnumerable<T>.OrderBy() every time they want to enumerate it. Apart from violating DRY, such an approach would also break encapsulation as consumers would have to know that my list is even sorted, which is really none of their business :)
The solution that seemed most logical/efficient was to expose IList<Child> as IEnumerable<Child> (to prevent List mutations) and add explicit Add/Remove methods to the containing Parent. This way, I can intercept changes to the List that necessitate a re-sort, and apply one via Linq:
public class Child {
public string StringProperty;
public int IntProperty;
}
public class Parent{
private IList<Child> _children = new List<Child>();
public IEnumerable<Child> Children{
get
{
return _children;
}
}
private void ReSortChildren(){
_children = new List<Child>(child.OrderBy(c=>c.StringProperty));
}
public void AddChild(Child c){
_children.Add();
ReSortChildren()
}
public void RemoveChild(Child c){
_children.Remove(c);
ReSortChildren()
}
}
Still, this approach doesn't intercept changes made to the underlying Child.StringProperty (which in this case is the property driving the sort). There must be a more elegant solution to such a basic problem, but I haven't been able to find one.
EDIT:
I wasn't clear in that I would preferable a LINQ compatible solution. I'd rather not resort to using .NET 2.0 constructs (i.e. SortedList)
What about using a SortedList<>?
One way you could go about it is to have Child publish an event OnStringPropertyChanged which passes along the previous value of StringProperty. Then create a derivation of SortedList that overrides the Add method to hookup a handler to that event. Whenever the event fires, remove the item from the list and re-add it with the new value of StringProperty. If you can't change Child, then I would make a proxy class that either derives from or wraps Child to implement the event.
If you don't want to do that, I would still use a SortedList, but internally manage the above sorting logic anytime the StringProperty needs to be changed. To be DRY, it's preferable to route all updates to StringProperty through a common method that correctly manages the sorting, rather than accessing the list directly from various places within the class and duplicating the sort management logic.
I would also caution against allowing the controller to pass in a reference to Child, which allows him to manipulate StringProperty after it's added to the list.
public class Parent{
private SortedList<string, Child> _children = new SortedList<string, Child>();
public ReadOnlyCollection<Child> Children{
get { return new ReadOnlyCollection<Child>(_children.Values); }
}
public void AddChild(string stringProperty, int data, Salamandar sal){
_children.Add(stringProperty, new Child(stringProperty, data, sal));
}
public void RemoveChild(string stringProperty){
_children.Remove(stringProperty);
}
private void UpdateChildStringProperty(Child c, string newStringProperty) {
if (c == null) throw new ArgumentNullException("c");
RemoveChild(c);
c.StringProperty = newStringProperty;
AddChild(c);
}
public void CheckSalamandar(string s) {
if (_children.ContainsKey(s))
var c = _children[s];
if (c.Salamandar.IsActive) {
// update StringProperty through our method
UpdateChildStringProperty(c, c.StringProperty.Reverse());
// update other properties directly
c.Number++;
}
}
}
I think that if you derive from KeyedCollection, you'll get what you need. That is only based on reading the documentation, though.
EDIT:
If this works, it won't be easy, unfortunately. Neither the underlying lookup dictionary nor the underlying List in this guy is sorted, nor are they exposed enough such that you'd be able to replace them. It might, however, provide a pattern for you to follow in your own implementation.
My entity has a property which is derived from other properties
public class MyClass
{
public DateTime Deadline { get; set; }
public Severity Severity
{
return (DateTime.Now - Deadline < new TimeSpan(5, 0, 0, 0)) ? Severity.High : Severity.Low;
}
}
is there a way I can modify the following
return repository.Query().Where(myClass => myClass.Severity == High);
so that the where clause evaluates in sql rather than in code?
I tried to do something like this, but to no avail
public class MyClass
{
public DateTime Deadline { get; set; }
public Func<MyClass, bool> SeverityFunc = (DateTime.Now - Deadline < new TimeSpan(5, 0, 0, 0)) ? Severity.High : Severity.Low;
public Severity Severity
{
return SeverityFunc.Invoke(this);
}
}
return repository.Query().Where(myClass => myClass.SeverityFunc(myclass) == High);
I'm guessing because the func cant be evaluated to SQL. Is there some other way to do this without ending up with duplicate calculations for severity
Any help appreciated, ta
Edit: This is a simplified version of what im trying to do, i'm looking for answers that cover the theory of this rather than a specific fix (though still welcome). Im interested in whats possible, and best practices to achieve this sort of thing.
Andrew
I have used something similar on a mapper. Make sure to wrap the Func on on Expr, like:
public Expr<Func<MyClass, bool>> SeverityFunc ...
By wrapping it with expr linq2sql will be able to look at the full expression and translate appropiately. I haven't used it as part of a class instance like the one you have, so I am not sure how that would affect it.
Regarding on where to put it, I had to move on the last time I worked on a similar scenario, in my case it ended up in the mapper, but mostly because it was more a mapping concern from an awfully database schema than domain logic. I didn't even had the property dynamically calculated on the domain entity for that matter (a different scenario for sure).
One option is the LINQ Dynamic Query Library, See http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Another option is to use the PredicateBuilder from http://www.albahari.com/nutshell/predicatebuilder.aspx
Hope this answers your question,
Roelof.
In VisualStudio (Pro 2008), I have just noticed some inconsistent behaviour and wondered if there was any logical reasoning behind it
In a WinForms project, if I use the line
if(myComboBox.Items[i] == myObject)
I get a compiler warning that I might get 'Possible unintended references' as I am comparing type object to type MyObject. Fair enough.
However, if I instead use an interface to compare against:
if(myComboBox.Items[i] == iMyInterface)
the compile warning goes away.
Can anyone think if there is any logical reason why this should happen, or just an artifact of the compiler not to check interfaces for comparison warnings. Any thoughts?
EDIT In my example, the combobox was bound to a List, but that list was generated using list<IMyInterface>.Cast<MyObject>().ToList<MyObject>()
Its as if the compiler is only taking still assuming I am binding to the List of IMyInterface.
(Object and Interface methods have been changed to protect the innocent)
The compile warning for the first sample is because any custom == operator for your class would be ignored and the references compared (maybe not what you intended, hence the warning).
It's not possible to specify that an operator should be overridden on an interface, so this will always be a reference comparison. The warning is not needed because you should always expect this.
Here's an example of overriding the == operator:
class Program
{
static void Main(string[] args)
{
object t1 = new MyTest() { Key = 1 };
MyTest t2 = new MyTest() { Key = 1 };
Console.WriteLine((MyTest)t1 == t2); // Uses overriden == operator, returns true
Console.WriteLine(t1 == t2); // Reference comparison, returns false
}
}
public class MyTest
{
public int Key { get; set; }
public override bool Equals(object obj)
{
return this.Key == (obj as MyTest).Key;
}
public override int GetHashCode()
{
return this.Key.GetHashCode();
}
public static bool operator ==(MyTest t1, MyTest t2)
{
return t1.Equals(t2);
}
public static bool operator !=(MyTest t1, MyTest t2)
{
return !t1.Equals(t2);
}
}
The MyTest class is considered equal if the Key property is equal. If you were to create an interface, you cannot specify that it should include a custom == operator and therefore the comparison would always be a reference comparison (and therefore false in the case of our sample code).
Lagerdalek,
The warning is generated because you need to cast the item from the Items collection back into the orginal type that was bound to the combo box, before comparing; otherwise you may get unexpected results as the compiler warns.
Here is an example:
myComboBox.DataSource = Collection<Car>;
So if the combo box is bound to a collection of car objects you would cast them back before comparison:
if((car)myComboBox.Items[i] == thisCar)
Then you shouldn't get any warnings.
Another method you could do is:
using(myComboBox.Items[i] as car){
if(myComboBox.Items[i] == thisCar)
}
Let me know. Good Luck! I'm going from memory, I hope I didn't mistype anything. :o)