C# Covariance issue - c#

I had a linq-to-sql generated domain entity that I cast to the proper interface like so:
public IEnumerable<IApplication> GetApplications()
{
using (var dc = new LqDev202DataContext())
{
return dc.ZApplications.Cast<IApplication>().ToList();
}
}
However I renamed the linq-to-sql table without touching my partial class and the code still compiled.
The list had the right amount of elements, but they were all null.
Do I need to write a helper method to make sure this will work, or is there a compile time safe simple built-in way to do this in .net 3.5?

You can also use this to do casting with conversions if needed:
public static IEnumerable<TDest> CastAll<TItem, TDest>(this IEnumerable<TItem> items)
{
var p = Expression.Parameter(typeof(TItem), "i");
var c = Expression.Convert(p, typeof(TDest));
var ex = Expression.Lambda<Func<TItem, TDest>>(c, p).Compile();
foreach (var item in items)
{
yield return ex(item);
}
}
From http://adventuresdotnet.blogspot.com/2010/06/better-more-type-safe-alternative-to.html

Related

Concatenating Lists at runtime

I am handling classes that wrap collections. For example:
public class CollA
{
public List<SomeType> Items {get;set;}
// other properties I'm not interested in
}
I am guaranteed that the collection classes will have exactly ONE property that is of List<T>
Now, I find myself with a requirement such that I may have many instances of CollA and I am asked to return a new instance of CollA where the property Items contains a union of the Items properties of the individual CollA instances. So, for example:
var A = new CollA(Items = new List<SomeType>
{
new SomeType("A"), new SomeType("B")
};
var B = new CollA(Items = new List<SomeType>
{
new SomeType("C"), new SomeType("D")
};
var result = SomeMythicalCombine(A, B);
// result.Items == { new SomeType("A"), new SomeType("B"), new SomeType("C"), new SomeType("D") }
This, if the types are all known at compile time is easy, but I need to do it with the types not being known until runtime.
I've got part of the way, I think, using reflection....
public T SomeMythicalCombine (params object[] collections)
{
var collectionType = typeof(T);
var listProperty = collectionType.GetProperties()
.Single(p=> typeof(IList).IsAssignableFrom(p.PropertyType));
var listPropertyName = listProperty.Name;
var result = Activator.CreateInstance(collectionType);
var innerType = listProperty.PropertyType.GenericTypeArguments[0];
var listType = typeof(List<>).MakeGenericType(innerType);
var list = Activator.CreateInstance(listType);
foreach(var collection in collections)
{
var listValues = collection.GetType().GetProperty(listPropertyName).GetValue(collection);
// listItems is an object here and I need to find a way of casting it
// to something I can iterate over so I can call (list as IList).Add(something)
}
// Then, I think, all I need to do is set the appropriate property on the
// the result item
result.GetType().GetProperty(listPropertyName).SetValue(result, list);
return result as T;
}
Can anyone fill in the gap in my thinking, please?
So basically if you know the type at compile time, you can do this:
var result = new CollA { Items = new[] { A, B }.SelectMany(c => c.Items).ToList() };
If you can require all your collection wrappers to implement an interface, it should be pretty simple to extract this into a generic method.
public interface ICollectionWrapper<T> { List<T> Items { get; set; } }
T SomeMythicalCombine<T, T2>(params T[] wrappers) where T : ICollectionWrapper<T2>, new()
{
return new T() { Items = wrappers.SelectMany(w => w.Items).ToList() };
}
That presupposes you can call the method with the right generic parameter. If your calling code knows the types of the collections you're dealing with, you can do this:
var result = SomeMythicalCombine(A, B);
But honestly if your calling code knows that, you might be better off using the first code snippet: it's concise and clear enough. Assuming you literally have a collection of objects that you just happen to know will all have the same run-time type, you should be able to use a little reflection to get that type and invoke the helper method with the right generic parameters. It's not ideal, but it might be faster/simpler than writing the entire method to work using reflection.
you can do this : var combined = A.Items.Concat(B.Items).
However, if the property is a part of interface or base class implementation, then you can target the implementation instead something like this :
public IList<TResult> SomeMythicalCombine<TResult>(params IInterface[] collection) // use interface or base class
{
// assuming that all collection would have the same element type.
}
if it is not a part of other implementations, then you can implement an interface and apply it to all classes, this would be an insurance that this collection will always be there as long as the class implements the interface.
if it's hard to achieve that, then you can and you see that reflection is your best option, you can use something like this :
// assuming all collections have the same property of type List<TResult> type.
// if they're different, then return an object instead. and change List<TResult> to IList
public IEnumerable<TResult> CombineLists<T, TResult>(params T[] instances)
where T : class
{
if (instances?.Any() == false) yield break;
foreach(var obj in instances)
{
if (obj == null) continue;
var list = obj.GetType()
.GetProperties()
.FirstOrDefault(p => typeof(List<TResult>).IsAssignableFrom(p.PropertyType))
?.GetValue(obj) as List<TResult>;
if (list?.Count == 0) continue;
foreach (var item in list)
yield return item;
}
}
usage :
var combined = CombineLists<CollA, string>(A, B);

Generic function throws cast exception [duplicate]

I've created two classes, with one of them having an implicit cast between them:
public class Class1
{
public int Test1;
}
public class Class2
{
public int Test2;
public static implicit operator Class1(Class2 item)
{
return new Class1{Test1 = item.Test2};
}
}
When I create a new list of one type and try to Cast<T> to the other, it fails with an InvalidCastException:
List<Class2> items = new List<Class2>{new Class2{Test2 = 9}};
foreach (Class1 item in items.Cast<Class1>())
{
Console.WriteLine(item.Test1);
}
This, however, works fine:
foreach (Class1 item in items)
{
Console.WriteLine(item.Test1);
}
Why is the implicit cast not called when using Cast<T>?
Because, looking at the code via Reflector, Cast doesnt attempt to take any implicit cast operators (the LINQ Cast code is heavily optimised for special cases of all kinds, but nothing in that direction) into account (as many .NET languages won't).
Without getting into reflection and other things, generics doesnt offer any out of the box way to take such extra stuff into account in any case.
EDIT: In general, more complex facilities like implicit/explict, equality operators etc. are not generally handled by generic facilities like LINQ.
You can also use this to do casting with conversions if needed:
public static IEnumerable<TDest> CastAll<TItem, TDest>(this IEnumerable<TItem> items)
{
var p = Expression.Parameter(typeof(TItem), "i");
var c = Expression.Convert(p, typeof(TDest));
var ex = Expression.Lambda<Func<TItem, TDest>>(c, p).Compile();
foreach (var item in items)
{
yield return ex(item);
}
}
From http://adventuresdotnet.blogspot.com/2010/06/better-more-type-safe-alternative-to.html
Thanks for that I was about to use that exact case somewhere. You have saved me a pile of time. As a possible solution to your problem you could use ConvertAll<> instead, like so:
foreach (Class1 item in items.ConvertAll<Class1>((i) => (Class1)i))
{
Console.WriteLine(item.Test1);
}
EDIT: or if you want to be more explicit that the cast is implicit then this works too:
foreach (Class1 item in items.ConvertAll<Class1>(i => i))
{
Console.WriteLine(item.Test1);
}
A solution could be to use a bit of linq'ing here if you really need this kind of conversion:
List items = new List{new Class2{Test2 = 9}};
foreach (Class1 item in (from x in items select (Class1)x))
{
Console.WriteLine(item.Test1);
}

Make call to generic class nested function

I have Repository<T> where T is derrived form of BaseEntity class.
Repository<T> has IQueryable<T> Table property. I need to make a call to FirstOrDefault method in the Table property.
till now i have got to list the Repositories, but stuck making call to the method using reflection.
private IEnumerable<object> GetEnumerableRepositoryOf<T>(params object[] constructorArgs) where T : class
{
List<object> objects = new List<object>();
foreach (Type type in
Assembly.GetAssembly(typeof(T)).GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(T))))
{
objects.Add(Activator.CreateInstance(typeof(Repository<>).MakeGenericType(type), constructorArgs));
}
return objects;
}
var repoList = GetEnumerableRepositoryOf<BaseEntity>(constructorArgs);
foreach (var repo in repoList)
{
// call FirstOrDefault() here
}
So your list is Repository<X> where X is T, and the property Table is IQueryable<X>. As we are lacking type knowledge of X at compile time, reflection it is.
So get the property with reflection by using the non-generic interface, then apply cast to T, and do your FirstOrDefault.
Here's how you can do it:
foreach (var repo in repoList)
{
var firstOrNull =
(repo.GetType().GetProperty("Table").GetValue(repo) as IQueryable)
.Cast<BaseEntity>().FirstOrDefault();
}
If using entity framework (which does not support this kind of casting above):
foreach (var repo in repoList)
{
var enumerator =
(repo.GetType().GetProperty("Table").GetValue(repo) as IEnumerable)
.GetEnumerator();
var firstOrNull = (BaseEntity) (enumerator.MoveNext() ?
enumerator.Current : default(BaseEntity));
}

LINQ filter combining exact matches like SQL IN and StartsWith matches

I'm having product entity:
public class Product : DomainBase
{
public virtual string Name { get; set; }
}
And there should be option to select products by filter, which contains an array of names, like:
public static IEnumerable<Product> SearchArrayQueryLinq(IEnumerable<string> names)
{
using (var session = Database.OpenSession())
{
var products = session.Query<Product>();
var result = products.Where(product => names.Any(name => product.Name.Contains(name)));
return result.ToList();
}
}
but it throws
System.NotSupportedException: Specified method is not supported.
What is right approach, to accomplish such filtering?
Without knowing more about what database you're connecting to or what library (is it RavenDB.. having done a quick Google?) then it's hard to be completely sure what the problem is.
However, what I think is happening is that you are giving an expression to the IQueryable "Where" extension method and the library is trying to turn that into search criteria to run against the db.. and failing because "Any" is not supported in nested criteria like that (again, I'm guessing).
The LINQ expressions that may or may not be translated into the database language (eg. SQL) vary by the library that performs the translation and vary by the database being talked to.
For example, the following (which is basically what you want to do) works fine with Entity Framework:
private static void Test(IEnumerable<string> names)
{
using (var context = new NORTHWNDEntities())
{
foreach (var product in context.Products.Where(product => names.Any(name => product.ProductName.Contains(name))))
{
Console.WriteLine(product.ProductName);
}
}
Console.ReadLine();
}
One easy option for you is to change your code to
public static IEnumerable<Product> SearchArrayQueryLinq(IEnumerable<string> names)
{
using (var session = Database.OpenSession())
{
var products = session.Query<Product>();
return result = products.ToList().Where(product => names.Any(name => product.Name.Contains(name)));
}
}
This should work.. however, it will get all Products from the database and perform the filtering in-memory. This is less efficient than getting the database to perform the search.
An alternative would be to generate an "Expression<Func<Product, bool>>" filter yourself that is easier for the library that you're using to translate. If, instead, of a nested "Any" criteria, you could generate a simple set of "OR" name checks then there is a better change of it working. The following will achieve that - but it's quite a lot of code. If this is something that you need to do in several places then some of the code could be made more general and reused.
private static IEnumerable<Product> SearchArrayQueryLinq(IEnumerable<string> names)
{
using (var context = new NORTHWNDEntities())
{
return context.Products.Where(GetCombinedOrFilter(names)).ToList();
}
}
private static Expression<Func<Product, bool>> GetCombinedOrFilter(IEnumerable<string> names)
{
var filter = GetNameFilter(names.First());
foreach (var name in names.Skip(1))
filter = CombineFiltersAsOr(filter, GetNameFilter(name));
return filter;
}
private static Expression<Func<Product, bool>> GetNameFilter(string name)
{
return product => product.ProductName.Contains(name);
}
private static Expression<Func<Product, bool>> CombineFiltersAsOr(Expression<Func<Product, bool>> x, Expression<Func<Product, bool>> y)
{
// Combine two separate expressions into one by combining as "Or". In order for this to work, instead of there being a parameter
// for each expression, the parameter from the first expression must be shared between them both (otherwise things will go awry
// when this is translated into a database query) - this is why ParameterRebinder.ReplaceParameters is required.
var expressionParameter = x.Parameters.Single();
return Expression.Lambda<Func<Product, bool>>(
Expression.Or(x.Body, ParameterRebinder.ReplaceParameters(y.Body, toReplace: y.Parameters.Single(), replaceWith: expressionParameter)),
expressionParameter
);
}
// Borrowed and tweaked from https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/
public sealed class ParameterRebinder : ExpressionVisitor
{
public static Expression ReplaceParameters(Expression expression, ParameterExpression toReplace, ParameterExpression replaceWith)
{
return new ParameterRebinder(toReplace, replaceWith).Visit(expression);
}
private readonly ParameterExpression _toReplace, _replaceWith;
private ParameterRebinder(ParameterExpression toReplace, ParameterExpression replaceWith)
{
_toReplace = toReplace;
_replaceWith = replaceWith;
}
protected override Expression VisitParameter(ParameterExpression p)
{
if (p == _toReplace)
p = _replaceWith;
return base.VisitParameter(p);
}
}
Update: I didn't notice your nhibernate tag - whoops! Using the criteria combining methods that nhibernate has is probably easier than all this.. :) I would have commented on your answer rather than updating my own but I haven't got the requisite 50 rep yet..
You are trying to mix both kinds of conditions and applying IEnumerable methods on string properties.
Your query should look like this:
var result = products.Where(product => names.Contains(product.Name));
to find exact matches.
For a combination of exact matches and StartsWith it should look like this:
var results = products.Where(product => (names.Contains(product.Name) || names.Any(name => name.StartsWith(product.Name))));
As I did a dive into NHibenrate documentation, it contains CriteriaAPI, so I came up to this
using (var session = Database.OpenSession())
{
var products = session.CreateCriteria<Product>();
if (names == null)
{
return products.List<Product>();
}
var orClause = Expression.Disjunction();
foreach (var name in names)
{
orClause.Add(Expression.Like(nameof(Product.Name), name, MatchMode.Start));
}
products.Add(orClause);
return products.List<Product>();
}

C# - Make a generic foreach method that accepts any type?

Im busy updating an entity using entity framework and web api (on the PUT method of the controller). For each collection property on the updated object, I loop through and check if each item exists in the collection on the existing object or not. If not, I add it.
The trouble is I have a lot of collections on the object and I find myself repeating the following code many times over.
Is there a way for me to wrap this into a generic method and pass that method the 2 collections to compare? Maybe by specifying the name of the property to check and primary key? How would I be able to specify the type for the foreach loop for example?
foreach (HBGender gender in updated.HBGenders)
{
HBGender _gender = existing.HBGenders.FirstOrDefault(o => o.GenderID == gender.GenderID);
if (_gender == null)
{
//do some stuff here like attach and add
}
}
return existing; //return the modified object
Thanks in advance. I hope this makes sense.
In its simplest form you could write an extension method as such:
public static class IEnumerableExtensionMethods
{
public static ICollection<T> ForEachAndAdd<T>(this IEnumerable<T> self,
ICollection<T> other,
Func<T, T, bool> predicate) where T : class
{
foreach(var h1 in self)
{
if(other.FirstOrDefault(h2 => predicate(h1, h2)) == null)
other.Add(h1);
}
return other;
}
}
Usage:
List<HBGender> updated = new List<HBGender>();
List<HBGender> existing = new List<HBGender<();
return updated.ForEachAndAdd(existing, (h1, h2) => h1.Gender == h2.Gender);
Note that if there is extra logic needed during an add, you could add an additonal Action<T> parameter to do so.
I don't know what you are trying to do, but you can play with this example:
List<object> a = new List<object>();
a.Add("awgf");
a.Add('v');
a.Add(4);
foreach (object b in a)
{
Type type = b.GetType().//Select more usefull
Convert.ChangeType(object,type);
}
Just pass your existing check function, as an extra parameter
public List<Class1> Find(List<Class1> updated, List<Class1> existing, Func<Class1, bool> predicate)
{
foreach (Class1 gender in updated)
{
Class1 _gender = existing.FirstOrDefault(predicate); //predicate for quoted example will be o => o.GenderID == gender.GenderID
if (_gender == null)
{
//do some stuff here like attach and add
}
}
return existing;
}

Categories