I actually asked a very similar question recently, but while the title mentioned classes, my content mostly referred to a tuple, and the (really great) answer reflected that. When I've tried to substitute a class in for the tuple, I get TargetParameterCountException: Parameter count mismatch. exception.
How can I get NHibernate to map to a Tuple or Class?
I have the following method to get a list of results from the database.
public static IList<T> Find<T>(DetachedCriteria crit) where T : class
{
lock (_locker)
{
return crit.GetExecutableCriteria(InstanceSession)
.List<T>();
}
}
This generally works well. However, I've changed a method that calls the method above from.
public IList<FooBarResult> FindResults(FooBarTask st)
{
return DataAccess.Find<FooBarResult>(DetachedCriteria.For<FooBarResult>()
.Add(Restrictions.Eq("Task", st))).ToList();
}
Which works, to this (as I don't want to return the whole of FooBarResult, just certain columns on it).
public IList<MyCustomClass> FindResults(FooBarTask st)
{
var typeConstructor = typeof(MyCustomClass).GetConstructors()[0];
return DataAccess.Find<MyCustomClass>(DetachedCriteria.For<FooBarResult>()
.Add(Restrictions.Eq("Task", st))
.SetProjection(
Projections.ProjectionList()
.Add(
Projections.Property("FieldOne") //this is a DateTime
)
.Add(
Projections.Property("FieldTwo") //this is a Guid
)
.SetResultTransformer(Transformers.AliasToBeanConstructor(typeConstructor))
)
);
}
And this is the class.
public class MyCustomClass
{
public MyCustomClass()
{
//placeholder
}
public MyCustomClass(DateTime FieldOne, Guid FieldTwo)
{
this.FieldOne = FieldOne;
this.FieldTwo = FieldTwo;
}
public DateTime FieldOne { get; set; }
public Guid FieldTwo { get; set; }
}
As mentioned earlier, when running the return crit.GetExecutableCriteria(InstanceSession).List<T>(); code I get a TargetParameterCountException: Parameter count mismatch. exception.
Is there any way I can make it return a list of my MyCustomClass?
I have not tested this, but you should use Transformers.AliasToBean take a look in to the aliases to let the transformer work:
public IList<MyCustomClass> FindResults(FooBarTask st)
{
var typeConstructor = typeof(MyCustomClass).GetConstructors()[0];
return DataAccess.Find<MyCustomClass>(DetachedCriteria.For<FooBarResult>()
.Add(Restrictions.Eq("Task", st))
.SetProjection(
Projections.ProjectionList()
.Add(Projections.Property("FieldOne"), "FieldOne")
.Add(Projections.Property("FieldTwo"), "FieldTwo")
)
.SetResultTransformer(Transformers.AliasToBean(typeof(MyCustomClass)))
.List<MyCustomClass>()
}
In this line:
var typeConstructor = typeof(MyCustomClass).GetConstructors()[0];
you will get the first, default constructor and its signature obviously doesn't match. The simplest fix for your case is:
var typeConstructor = typeof(MyCustomClass).GetConstructors()[1];
But the cleanest solution would be something along these lines (untested and also a bit simplified):
var typeConstructor = GetMatchingConstructorOrThrow<MyCustomClass>
(typeof(DateTime), typeof(Guid));
// ...
private ConstructorInfo GetMatchingConstructorOrThrow<T>(params Type[] requiredSignature)
where T : class
{
foreach (var c in typeof(T).GetConstructors())
{
var currentSignature = c.GetParameters().Select(p => p.ParameterType);
if (currentSignature.SequenceEqual(requiredSignature))
{
return c;
}
}
throw new NoMatchingConstructorFoundException();
}
Related
Per the code below, I have a GetOrderPreviewSecurity() method that returns a Security, which is a parent class to Stock and MutualFund.
The GetOrderPreviewSecurity() method only actually returns a Stock type or MutualFund type to a Security property. The issue I'm having is trying to access the Child-specific properties as described in the //comments below.
Is there a way I can force the cast or clean this up that is cleaner than "var newThing = (ChildClass)SecurityClass;" and using newThing?
public class Stock : Security
{
public string Ask;
public string Bid;
}
public class MutualFund : Security
{
public string AssetClass;
public string Category;
}
public Security PreviewSecurity;
public Security GetOrderPreviewSecurity(_orderTickerText){
//Do stuff
if (boolean thing)
return new Stock();
else if (boolean thing)
return new MutualFund();
else
return new Security("empty");
}
//Some stuff
private void ExecutePreviewOrder()
{
if (!string.IsNullOrEmpty(_orderTickerText) && _orderShareQuantity > 0)
{
//Returns a **Security**
PreviewSecurity = _portfolioService.GetOrderPreviewSecurity(_orderTickerText);
if (PreviewSecurity is Stock)
{
//PreviewSecurity is still a Security type.
//No Bid or Ask properties available
PreviewBid = PreviewSecurity.Bid;
PreviewAsk = PreviewSecurity.Ask;
}
else if (PreviewSecurity is MutualFund)
{
//PreviewSecurity is still a Security type.
//No AssetClass or Category propeties available
PreviewAssetClass = PreviewSecurity.AssetClass;
PreviewCategory = PreviewSecurity.Category;
}
}
}
You need to have a set of parentheses around the entire thing, like this:
if (PreviewSecurity is Stock)
{
PreviewBid = ((Stock)PreviewSecurity).Bid;
PreviewAsk = ((Stock)PreviewSecurity).Ask;
}
else if (PreviewSecurity is MutualFund)
{
PreviewAssetClass = ((MutualFund)PreviewSecurity).AssetClass;
PreviewCategory = ((MutualFund)PreviewSecurity).Category;
}
I think the issue is that the dot has higher precedence than the cast operator, so first the dot operator is applied, and then the cast operator is applied to whatever is returned by the dot operator.
I do not see any problem with casting the Security back once you have type checked it.
if (PreviewSecurity is Stock)
{
PreviewBid = (Stock)PreviewSecurity.Bid;
PreviewAsk = (Stock)PreviewSecurity.Ask;
}
else if (PreviewSecurity is MutualFund)
{
PreviewAssetClass = (MutualFund)PreviewSecurity.AssetClass;
PreviewCategory = (MutualFund)PreviewSecurity.Category;
}
I have the following:
public class Broadcast {
public int NumUsersToMessage { get; set; }
public int NumMessagesQueued { get; set; }
public string DbUsersMessaged { get; set; }
public int NumMessagesSent {
get {
return UsersMessaged.Count();
}
}
public List<int> UsersMessaged {
get {
return DbUsersMessaged == null ? new List<int>() : DbUsersMessaged.Split(',').Select(Int32.Parse).ToList();
}
set {
DbUsersMessaged = value != null ? String.Join(",", value) : null;
}
}
}
My goal here is to only ever access DbUsersMessaged through UsersMessaged. I'm attempting to do broadcast.UsersMessaged.Add(2), however since this is not an assignment, I can't get the property to behave as I like. Instead, I have to do this:
tempList = broadcast.UsersMessaged();
tempList.Add(2);
broadcast.UsersMessaged = tempList;
db.SaveChanges();
Which is obviously unwieldy. I'm considering making an AddReassign extension method but I want to know - what's the standard practice here for supporting Lists of primitive types? It looks like even with the extension method, my best shot looks like this:
broadcast.UsersMessaged = broadcast.UsersMessaged.AddReassign(2) // yuck!
Before anyone asks - we've intentionally denormalized this for performance reasons.
If you don't care about performance, you can create own list:
public class MyList : IList<int>
{
private List<int> underlyingList;
private Broadcast entity;
public MyList(Broadcast entity)
{
this.entity = entity;
this.underlyingList = entity.DbUsersMessaged?.Split(",") ?? new List<int>();
}
public void Add(int i)
{
this.underlyingList.Add(i);
this.entity.DbUsersMessaged = String.Join(",", underylingList);
}
// other interface memebers impl
}
Then
MyList list;
public IList<int> UsersMessaged {
get {
return myList ?? (myList = new MyList(this));
}
}
Of course it is only sample.
I recommend you to have a look at this: Entity Framework 5 - Looking for Central Point to Execute Custom Code after Entity is Loaded from Database
And then convert from string to list, and then use Saving Changes event to convert back into the string construction when saving.
Then, for performance, maybe you want to use byte[] rather than a string for storing the data in the database.
Question:
Can anyone tell me why my unit test is failing with this error message?
CollectionAssert.AreEquivalent failed. The expected collection contains 1
occurrence(s) of . The actual
collection contains 0 occurrence(s).
Goal:
I'd like to check if two lists are identical. They are identical if both contain the same elements with the same property values. The order is irrelevant.
Code example:
This is the code which produces the error. list1 and list2 are identical, i.e. a copy-paste of each other.
[TestMethod]
public void TestListOfT()
{
var list1 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
var list2 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
}
public class MyPerson
{
public string Name { get; set; }
public int Age { get; set; }
}
I've also tried this line (source)
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
and this line (source)
CollectionAssert.AreEquivalent(list1.ToArray(), list2.ToArray());
P.S.
Related Stack Overflow questions:
I've seen both these questions, but the answers didn't help.
CollectionAssert use with generics?
Unit-testing IList with CollectionAssert
You are absolutely right. Unless you provide something like an IEqualityComparer<MyPerson> or implement MyPerson.Equals(), the two MyPerson objects will be compared with object.Equals, just like any other object. Since the objects are different, the Assert will fail.
It works if I add an IEqualityComparer<T> as described on MSDN and if I use Enumerable.SequenceEqual. Note however, that now the order of the elements is relevant.
In the unit test
//CollectionAssert.AreEquivalent(list1, list2); // Does not work
Assert.IsTrue(list1.SequenceEqual(list2, new MyPersonEqualityComparer())); // Works
IEqualityComparer
public class MyPersonEqualityComparer : IEqualityComparer<MyPerson>
{
public bool Equals(MyPerson x, MyPerson y)
{
if (object.ReferenceEquals(x, y)) return true;
if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) return false;
return x.Name == y.Name && x.Age == y.Age;
}
public int GetHashCode(MyPerson obj)
{
if (object.ReferenceEquals(obj, null)) return 0;
int hashCodeName = obj.Name == null ? 0 : obj.Name.GetHashCode();
int hasCodeAge = obj.Age.GetHashCode();
return hashCodeName ^ hasCodeAge;
}
}
I was getting this same error when testing a collection persisted by nHibernate. I was able to get this to work by overriding both the Equals and GetHashCode methods. If I didn't override both I still got the same error you mentioned:
CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of .
The actual collection contains 0 occurrence(s).
I had the following object:
public class EVProjectLedger
{
public virtual long Id { get; protected set; }
public virtual string ProjId { get; set; }
public virtual string Ledger { get; set; }
public virtual AccountRule AccountRule { get; set; }
public virtual int AccountLength { get; set; }
public virtual string AccountSubstrMethod { get; set; }
private Iesi.Collections.Generic.ISet<Contract> myContracts = new HashedSet<Contract>();
public virtual Iesi.Collections.Generic.ISet<Contract> Contracts
{
get { return myContracts; }
set { myContracts = value; }
}
public override bool Equals(object obj)
{
EVProjectLedger evProjectLedger = (EVProjectLedger)obj;
return ProjId == evProjectLedger.ProjId && Ledger == evProjectLedger.Ledger;
}
public override int GetHashCode()
{
return new { ProjId, Ledger }.GetHashCode();
}
}
Which I tested using the following:
using (ITransaction tx = session.BeginTransaction())
{
var evProject = session.Get<EVProject>("C0G");
CollectionAssert.AreEquivalent(TestData._evProjectLedgers.ToList(), evProject.EVProjectLedgers.ToList());
tx.Commit();
}
I'm using nHibernate which encourages overriding these methods anyways. The one drawback I can see is that my Equals method is based on the business key of the object and therefore tests equality using the business key and no other fields. You could override Equals however you want but beware of equality pollution mentioned in this post:
CollectionAssert.AreEquivalent failing... can't figure out why
If you would like to achieve this without having to write an equality comaparer, there is a unit testing library that you can use, called FluentAssertions,
https://fluentassertions.com/documentation/
It has many built in equality extension functions including ones for the Collections. You can install it through Nuget and its really easy to use.
Taking the example in the question above all you have to write in the end is
list1.Should().BeEquivalentTo(list2);
By default, the order matters in the two collections, however it can be changed as well.
I wrote this to test collections where the order is not important:
public static bool AreCollectionsEquivalent<T>(ICollection<T> collectionA, ICollection<T> collectionB, IEqualityComparer<T> comparer)
{
if (collectionA.Count != collectionB.Count)
return false;
foreach (var a in collectionA)
{
if (!collectionB.Any(b => comparer.Equals(a, b)))
return false;
}
return true;
}
Not as elegant as using SequenceEquals, but it works.
Of course to use it you simply do:
Assert.IsTrue(AreCollectionsEquivalent<MyType>(collectionA, collectionB, comparer));
I have a problem with returning a list by executing a Select LINQ query. This is the query:
var data = Repository<EducationString>
.Find()
.ToList()
.Select(p => new EducationStringModel() {
Id = p.Id,
Title = p.Title,
EducationDegree=p.EducationDegree })
.ToList();
As you can see I used ToList() 2 times. I don't know why but when I delete the first ToList() I see this error, "Index was outside the bounds of the array", but by having both ToList() there is no problem.
Would it help if I said EducationDegree in EducationStringModel is an IList<EducationDegree>?
Is there anybody who knows the reason?
#Mark :its L2O
if u need to see the classes:
public class EducationStringModel
{
private IList _educationDegree = new List();
public IList EducationDegree
{
get
{
if (_educationDegree == null)
{
_educationDegree = new List();
}
return _educationDegree;
}
set { _educationDegree = value; }
}
public int? Id { get; set; }
public string Title { get; set; }
}
public class EducationString{
private string _title;
private IList _educationExperiences;
private IList _educationDegree;
virtual public string Title
{
get { return _title; }
set { _title = value; }
}
virtual public IList<EducationExperience> EducationExperiences
{
get
{
if (_educationExperiences == null)
{
_educationExperiences = new List<EducationExperience>();
}
return _educationExperiences;
}
set
{
_educationExperiences = value;
}
}
virtual public IList<EducationDegree> EducationDegree
{
get
{
if (_educationDegree == null)
{
_educationDegree = new List<EducationDegree>();
}
return _educationDegree;
}
set
{
_educationDegree = value;
}
}
}
Is that the actual code? The only unclear thing there is: what does Find() return?
It sounds like the ToList is helping here by breaking composition and using LINQ-to-Objects, in which case AsEnumerable() should work just as well. After that you just do a Select (which for L2O just takes each item in turn and applies the map). If Find() is something more exotic, it sounds like a bug in that LINQ provider (or perhaps more fairly: that provider struggling to cope with an atypical construct). Hard to say more without a fully reproducible example.
UPDATE: I should have mentioned in the original post that I want to learn more about generics here. I am aware that this can be done by modifying the base class or creating an interface that both document classes implement. But for the sake of this exercise I'm only really interested in solutions that do not require any modification to the document classes or their base class. I thought that the fact that the question involves extension methods would have implied this.
I have written two nearly identical generic extension methods and am trying to figure out how I might refactor them into a single method. They differ only in that one operates on List and the other on List, and the properties I'm interested in are AssetID for AssetDocument and PersonID for PersonDocument. Although AssetDocument and PersonDocument have the same base class the properties are defined in each class so I don't think that helps. I have tried
public static string ToCSVList<T>(this T list) where T : List<PersonDocument>, List<AssetDocument>
thinking I might then be able to test the type and act accordingly but this results in the syntax error
Type parameter 'T' inherits
conflicting constraints
These are the methods that I would like to refactor into a single method but perhaps I am simply going overboard and they would best be left as they are. I'd like to hear what you think.
public static string ToCSVList<T>(this T list) where T : List<AssetDocument>
{
var sb = new StringBuilder(list.Count * 36 + list.Count);
string delimiter = String.Empty;
foreach (var document in list)
{
sb.Append(delimiter + document.AssetID.ToString());
delimiter = ",";
}
return sb.ToString();
}
public static string ToCSVList<T>(this T list) where T : List<PersonDocument>
{
var sb = new StringBuilder(list.Count * 36 + list.Count);
string delimiter = String.Empty;
foreach (var document in list)
{
sb.Append(delimiter + document.PersonID.ToString());
delimiter = ",";
}
return sb.ToString();
}
Your implementation is basically reimplementing string.Join method, so you might try to make it simpler and more generic with some LINQ:
public static string ToCSVList<T>(this IEnumerable<T> collection)
{ return string.Join(",", collection.Select(x => x.ToString()).ToArray()); }
public static string ToCSVList(this IEnumerable<AssetDocument> assets)
{ return assets.Select(a => a.AssetID).ToCSVList(); }
public static string ToCSVList(this IEnumerable<PersonDocument> persons)
{ return persons.Select(p => p.PersonID).ToCSVList(); }
I think the way would be to let PersonDocument and AssetDocument inherit from a Document class, which would have an Id property, that stores your current PersonId or AssetId respectivly.
Make an abstraction, such as IDocument or an abstract class BaseDocument which exposes the id (which is the only field you are really using) and make both PersonDocument and AssetDocument implement that. Now make your generic method accept IDocument or BaseDocument instead.
How do you like this variant (a little bit simplified, but you should get the idea):
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var la = new List<AssetDocument> { new AssetDocument() {AssetID = 1} };
var result = la.ToCSVList(l => l.AssetID.ToString());
}
}
public class AssetDocument
{
public int AssetID { get; set; }
}
public static class GlobalExtensions
{
public static string ToCSVList<T>(this List<T> list, Func<T, string> propResolver)
{
var sb = new StringBuilder(list.Count * 36 + list.Count);
var delimiter = "";
foreach (var document in list)
{
sb.Append(delimiter);
sb.Append(propResolver(document));
delimiter = ",";
}
return sb.ToString();
}
}
}
This would work with any list (in case you don't care about the preallocated memory in StringBuilder even with any IEnumerable).
Update: Even if you want to keep your original extension methods, you can reduce them to one line of code with this.
What about making your method also take in a delegate to return the document.AssetID.ToString() for that list as appropriate?
Using Lamda expressions this could be reasonably lightweight, if a little ugly. A console application to demonstarate:
class Program
{
static void Main(string[] args)
{
List<string> strings = new List<string> { "hello", "world", "this", "is", "my", "list" };
List<DateTime> dates = new List<DateTime> { DateTime.Now, DateTime.MinValue, DateTime.MaxValue };
Console.WriteLine(ToCSVList(strings, (string s) => { return s.Length.ToString(); }));
Console.WriteLine(ToCSVList(dates, (DateTime d) => { return d.ToString(); }));
Console.ReadLine();
}
public static string ToCSVList<T, U>(T list, Func<U, String> f) where T : IList<U>
{
var sb = new StringBuilder(list.Count * 36 + list.Count);
string delimiter = String.Empty;
foreach (var document in list)
{
sb.Append(delimiter + f(document));
delimiter = ",";
}
return sb.ToString();
}
}
Whether this is the best approach or not, I leave as an exercise for the reader!
I only know java, so I can't give correct syntax, but the general approach should work:
define an interface Document, which gets implemented by PersonDocument and AssetDocument,
with the method
String getIdString();
Use a List as a parameter to you method. Note this is java syntax for a List of Something that inherits/extends from Document.
You could use Reflection for a bit of Duck Typing action!
I have assumed that your classes are called #class#Document and you want to concatenate the #class#ID properties. If the list contains classes that conform to this naming they will be concatenated. Otherwise they wont.
This is very much how the Rails framework operates, using Convention over Configuration.
Obviously such behaviour is more suited to dynamic languages such as Ruby. Probably the best solution for a more static language such as C# would be to refactor the base classes, use interfaces etc.. But that wasnt in the spec, and for educational purposes this is one way around things!
public static class Extensions
{
public static string ToCSVList<T> ( this T list ) where T : IList
{
var sb = new StringBuilder ( list.Count * 36 + list.Count );
string delimiter = String.Empty;
foreach ( var document in list )
{
string propertyName = document.GetType ().Name.Replace("Document", "ID");
PropertyInfo property = document.GetType ().GetProperty ( propertyName );
if ( property != null )
{
string value = property.GetValue ( document, null ).ToString ();
sb.Append ( delimiter + value );
delimiter = ",";
}
}
return sb.ToString ();
}
}
Usage (note no need for inheritance with Duck Typing - also works with any type!) :
public class GroovyDocument
{
public string GroovyID
{
get;
set;
}
}
public class AssetDocument
{
public int AssetID
{
get;
set;
}
}
...
List<AssetDocument> docs = new List<AssetDocument> ();
docs.Add ( new AssetDocument () { AssetID = 3 } );
docs.Add ( new AssetDocument () { AssetID = 8 } );
docs.Add ( new AssetDocument () { AssetID = 10 } );
MessageBox.Show ( docs.ToCSVList () );
List<GroovyDocument> rocs = new List<GroovyDocument> ();
rocs.Add ( new GroovyDocument () { GroovyID = "yay" } );
rocs.Add ( new GroovyDocument () { GroovyID = "boo" } );
rocs.Add ( new GroovyDocument () { GroovyID = "hurrah" } );
MessageBox.Show ( rocs.ToCSVList () );
...