Create LINQ query at runtime to GroupBy in EntityFramework (with inheritance) - c#

This is the scenario. I have the following three classes, they are defined in Entity Framework, i only define them here for the example:
public class Foo
{
public string Color { get; set; }
}
public class Bar : Foo
{
public string Height { get; set; }
}
public class Pipe : Foo
{
public string Width { get; set; }
}
So, I have many Foo's, this is my base class, I want to be able to specify a propery, and do this query:
from e in Context.Foos
group e.Color by e.Color into result
select new
{
Value = result.Key,
ValueCount = result.Count()
}
This should end up with:
Blue 2
Black 4
Yellow 2
This works, however I want to specify this at run time, with the Property name 'Color' passed by the client. Also, I want to search the derived entities too. If i try to do
group e.Height by e.Height into result
It wont work because there is no Height in Foo, only in Bar. But the point is I ONLY want to return Bars, this should also be specified at runtime. This is the main problem I have been having. I cant do Foos.OfType<Bar>.GroupBy(some dynamic stuff) because I dont know the type to filter for at runtime.
Would really appreciate some help on this matter.
EDIT
Basically, what i'm trying to do is this System.LINQ.Dynamic: Select(" new (...)") into a List<T> (or any other enumerable collection of <T>) but return Count instead of Sum at the end.

In this answer, a func is being used to create a dynamic Where.
private List<T> GetResults<T>(IQueryable<T> source,
Expression<Func<T, bool>> queryFunction)
{
return source.Where(queryFunction).ToList<T>();
}
You should be able to do something similar with GroupBy.

Related

c# methods that takes an Interface?

I am new to Interfaces.
I have a lot of objects that I pass as DTOs through my layers to the UI. Some of them are quite complex (Quite a few properties), but I only want to use them, in certain circumstances, in DropDown lists. These DTOs all have an int Id, and a string Description.
I would like to create a static function that takes a List<> of one of these objects, and returns a List<SelectListItem>
So, I am trying to use Interfaces for the first time.
I created an Interface:
public interface IListableItem
{
int Id { get; set; }
string Description { get; set; }
}
And then, I assigned that interface to one of my DTO objects I am trying to convert:
public class CategoryDto : BaseDto , IListableItem
{
public int PortfolioId { get; set; }
public string Description { get; set; }
public List<ExtendedSubCategoryDto> SubCategories { get; set; }
public bool IsExpenseCategory { get; set; }
public CategoryDto()
{
SubCategories = new List<ExtendedSubCategoryDto>();
}
}
Then, I created my generic method that takes a list of the category dtos, and will hopefully return a list
public static List<SelectListItem> TranslateToSelectList(List<IListableItem> source)
{
var reply = source.Select(item => new SelectListItem
{
Value = item.Id.ToString(CultureInfo.InvariantCulture), Text = item.Description
}).ToList();
return reply;
}
But, when I attempt to use this method, passing it a List, it fails.
model.Categories =
Translator.TranslateToSelectList(MyService.GetCategoriesByPortfolioId());
GetCategoriesByPortfolioId returns a List.
It's failing with the error:
CategoryDto is not assignable to IListableItem
It's probably a basic Interface understanding issue on my part, but what am I doing wrong, and how can I fix it?
If your method expects List<IListableItem>, you can't pass List<CategoryDTO>.
If that would be possible, you could Add different instances of elements that implement the interface IListableItem into a collection that is holding CategoryDTO elements, and read them.
Ultimately, that wouldn't make sense.
You can fix it, if you use IEnumerable interface. That allows covariance(going from higher type to lower type in generic type parameter).
The reason it works, is that IEnumerable is a "read-only view" of collection, thus you can't really add anything to it - plus what's important, the type parameter is marked as covariant.
public static List<SelectListItem> TranslateToSelectList(
IEnumerable<IListableItem> source)
{..}

Relate two lists with LINQ extensions

I have two lists of different objects, one from a third party API and one from my database - and I'm trying to link the two as a relationship. Ideally with a similar effect of how DBML's create relationships for tables with foreign keys (Customer.Orders).
From third party:
class ApiObject {
public string ID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
... 30 other properties ...
}
From my database:
class DbmlObject {
public int ID { get; set; }
public string ApiID { get; set; }
public string OtherString { get; set; }
}
They are related through ApiObject.ID == DbmlObject.ApiID
I do not want to merge these, nor join them into some anonymous object (and explicitly list 30+ properties) - but rather to make the DbmlObject a linked property of ApiObject. i.e.: addressable as:
apiObject.DbmlObjects.First().OtherString or ideally apiObject.DbmlObject.OtherString since it is a 1 to 1 relationship.
In controller:
List<ApiObject> apiObjects = _thirdParty.GetObjects();
DbmlDataContext model = new DbmlDataContext();
List<DbmlObject> dbmlObjects = model.GetAllDbmlObjects();
// relate them here
foreach (var apiObject in apiObjects)
Console.Write(apiObject.DbmlObject.OtherString)
// NOTE: ideally this foreach loop should not make a DBML query on each iteration, just the single GetAllDbmlObjects query above.
It sounds like a join:
var combined = from api in apiObjects
join dbml in dbmlObjects on api.ID equals dbml.ApiID
select new { api, dbml }
In order to get DbmlObject "in" the ApiObject, you will need to either inherit ApiObject and construct a new one of that class, which includes the Dbml property, or create a entirely new class to return. If you need static typing this is the best you can do - of course you could (mis)use dynamic to get what you want.
In this case, you are mentioning (in comments) that the ApiObject class is from a third party library that you can't change - in this case I would probably choose to create a new type which takes an instance of both objects in the constructor and exposes the properties you need - a decorator. Yes, it looks like a lot of code, but it is not complex, good tools will autogenerate it for you - and you get the class that you need for your code to be succinct.
In case you want to go further with returning an IEnumerable<dynamic>, you could build a "combining dynamic" object based on DynamicObject that then responds to all the properties of ApiObject and DbmlObject - or just adds DbmlObject as a property. I am not saying this is the right way to go, it depends on what you need it for - remember you are losing type safety. Here is a simple example:
void Main()
{
dynamic dyn = new CombiningDynamic(new Foo { X = 3 }, new Bar { Y = 42 });
Console.WriteLine(dyn.X);
Console.WriteLine(dyn.Y);
}
public class Foo
{
public int X {get;set;}
}
public class Bar
{
public int Y { get;set;}
}
public class CombiningDynamic : DynamicObject
{
private object [] innerObjects;
public CombiningDynamic(params object [] innerObjects)
{
this.innerObjects = innerObjects;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach(var instance in innerObjects)
{
Type t = instance.GetType();
PropertyInfo prop = t.GetProperty(binder.Name);
if (prop != null && prop.CanRead)
{
result = prop.GetValue(instance, null);
return true;
}
}
result = null;
return false;
}
}
Remember, this is example code. If you really go this way, you would want to perhaps override some more of the methods (TrySetMember, ...), and you most definetely would want to cache the reflection results so you don't need to walk the types each time - reflection is (comparatively) slow.

Use only one method that accepts different typed parameters

I've 3 different database tables that have the same 5 fields but those does not have any foreign key relation as they are not keeping the same value in fact, but the equivalents; like: CompanyA table has productA and CompanyB has productB.
so I have 3 different collections include 3 fields that are equivalent. So what I'd like to do is to use a single class that has companyType and ProductName properties and use only one method to cast those 3 different collections to one and only class object, say ResultClass.
public class ResultClass
{
public EnumCompanyType CompanyType { get; set; }
public string ProductName { get; set; }
public ICollection<ResultClass> ConvertAnything(ICollection<T> collection)
{
//Cast it to ResultClass
return resultClassCollection;
}
}
So that I can use this like:
ICollection<ProductA> aCollection = GetCompanyAData();
ICollection<ProductB> bCollection = GetCompanyBData();
ConvertAnything(aCollection);
ConvertAnything(bCollection);
I've tried "dynamic" but actually don't know the principle (neither have the knowledge); so I've messed it up and I think it's not for this stuff.
I've tried to create an extension method but since the extension has no type for its parameter (as it is using ICollection), I can't access the fields of the items (eg. properties)
I'm using LinqToSql and all the database table terms etc. belongs to this concept, nothing else.
edit:
I think I should made myself clear:
The multiple instances that I'm trying to avoid (or shouldn't I, still thinking) is like below
public ICollection<ResultClass> ConvertAnythingForA(ICollection<ProductA> collection)
{
foreach(var item in collection)
{
var result = new ResultClass
{
ProductName = item.ProductA,
ProductType = EnumProductType.ProductA
};
resultClassCollection.Add(result);
}
return resultClassCollection;
}
public ICollection<ResultClass> ConvertAnythingForB(ICollection<ProductB> collection)
{
foreach(var item in collection)
{
var result = new ResultClass
{
ProductName = item.ProductB,
ProductType = EnumProductType.ProductB
};
resultClassCollection.Add(result);
}
return resultClassCollection;
}
Thanks in advance.
I may not be understanding you completely, but since ProductA, ProductB etc have the same signature it seems like you'd want an interface like
public interface IResultClass
{
int CompanyType { get; set; }
string ProductName { get; set; }
}
And have those classes just implement the interface. You could work with collections of the interface that could have objects of the various types. If you need a convert anything method, it would look like
public ICollection<IResultClass> ConvertAnything<T>(ICollection<T> collection) where T : IResultClass
{
return collection.Select(x => (IResultClass)x).ToList();
}
After comments- I see you that you are getting a non generic ICollection. Did you try something like this:
public ICollection<IResultClass> ConvertAnything(ICollection collection)
{
var x = collection.Cast<IResultClass>();
return x.ToList();
}
You may want to use function overloading. This example uses different numbers of parameters, but you could just as easily use different types instead.
http://csharp.net-tutorials.com/classes/method-overloading/
If both datasets are equivalent, why not just have one type called ICollection<Product>? And one function, eg. "GetProductData("A")", where "A"/"B" is the parameter? Or am I missing something?

Is it possible to modify an IQueryable expression manually

I'm pretty certain I know the answer is no but as a last ditch attempt I thought I'd ask the question here.
I'm using EF code first to query a table in the usual fashion
_context.Set<Foo>().Where(f => f.Bar == 999);
which creates the following expression (I've just written this so it might be wrong).
{SELECT
[Extent1].[Test] AS [Test],
[Extent1].[Test2] AS [Test2],
FROM [dbo].[Foo] AS [Extent1]
WHERE 19 = [Extent1].[Bar]}
Now, is it possible to manually modify this query to change the table name to, say, Foo10? (probably not)
Failing that, does anybody know of a way I can "late bind" the table name in code first?
You're probably wondering "Why the dirty hack?" As usual, this is a legacy issue with a database that's got some design issues and can't be changed.
Thanks in advance.
Ps. I'm aware that I could use Database.SqlQuery but would rather not.
Why don't you use TPT inheritance on your model?
Similar to #Krizz's answer, but you avoid using dynamic LINQ.
Using your comment:
if a particular parameter has a value of 1 look in Foo1 if its 2 look in Foo2 and so on
So, you could do this:
var query = ctx
.Foos
.OfMyType(value)
.Where(f => f.Bar == 999) // f.Bar is on the base/abstract entity.
.ToList();
Where OfMyType is a custom extension method on IQueryable<T>:
public static IQueryable<T> OfMyType<T>(this IQueryable<T> source, string value)
{
switch (value)
{
case "1":
return source.OfType<Foo1>();
case "2":
return source.OfType<Foo2>();
// etc, etc
}
}
Most (if not all) of the properties will be on the abstract "Foo" entity, and you create derived entities for each of the tables, which each have their own backing table.
That way, "consuming" code (e.g the ones making the queries), need not care about the different tables/Foo's, they simply pass the "magic value" to your repository (hopefully your using one), then you can silently switch to the table you want.
Would that work?
Assuming you have reasonable number of tables, I would add them all into model and create a common interface all classes will implement and then select the adequate model and use Dynamic Linq for querying.
I am not sure if this works, haven't checked it and haven't worked with "EF code-first", but this is something I would try:
Let's say your table(s) Foo have fields - Bar, Pub, X and let X be the one which the respective table depends on?
Then, I would define interface:
interface IFoo
{
int Bar { get; set; }
string Pub { get; set; }
int X { get; set; }
}
Then each table will have its class in model:
[Table("Foo1")]
class Foo1 : IFoo
{
public int Bar { get; set; }
public string Pub { get; set; }
public int X { get; set; }
}
[Table("Foo2")]
class Foo2 : IFoo
{
public int Bar { get; set; }
public string Pub { get; set; }
public int X { get; set; }
}
Then you could filter them like following:
IQueryable GetAdequateFoo(int X)
{
switch (X) // you could use reflection here to dynamically call the given Set<Foo#>()
{
case 1:
return _context.Set<Foo1>();
case 2:
return _context.Set<Foo2>();
default:
return null;
}
}
IFoo GetFooByBarAndX(int bar, int X)
{
IQueryable context = GetAdequateFoo(X);
return context.Where("it.Bar == #0", bar).Cast<IFoo>();
}
Here is how you create a new IQueryable with a new/modified expression (EF core 5.0 at the time of this writing).
var expression = query.Expression;
//modify your expression usually by building a new one or rebuilding using an ExpressionVisitor
var newQuery = query.Provider.CreateQuery(expression);
Note: I was searching for editing an Expression on an IQueryable and this is the question that came first, but the details then focus on a very specific use case and the more general question hasn't been answered...

How do I use Reflection to set a Property with a type of List<CustomClass>

There is already a similar question but it didn't seem to ask about the situation that the question implies.
The user asked about custom classes in a list but his list object is of type string.
I have a class Foo that has a list of Bars:
public class Foo : FooBase
{
public List<Bar> bars {get; set;}
public Foo() {}
}
public class Bar
{
public byte Id { get; set; }
public byte Status { get; set; }
public byte Type { get; set; }
public Bar(){}
}
I instantiate Foo using reflection via Activator.CreateInstance(). Now I need to populate that list of bars with Bar objects.
Foo is obtained using
Assembly.GetAssembly(FooBase).GetTypes().Where(type => type.IsSubclassOf(FooBase));
Bar is a public class in the same Assembly. I'll need to get at that type somehow. I can't seem to see what the type of the list contained in Foo is. I know it's a list though. I'm seeing the list property as List`1.
I'd need to see what type of object the list holds and handle that accordingly.
The text
List`1
is the way that generics are written under the bonnet - meaning "List with 1 generic type arg, aka List<>". If you have a PropertyInfo, you should be set; this will be the closed generic List<Bar>. Is it that you want to find the Bar given just this?
If so, this is discussed in various questions, including this one; to duplicate the key bit (I prefer to code against IList<T>, since it handles a few edge-cases such as inheriting from List<T>):
static Type GetListType(Type type) {
foreach (Type intType in type.GetInterfaces()) {
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IList<>)) {
return intType.GetGenericArguments()[0];
}
}
return null;
}
var prop = footype.GetProperty("bars");
// In case you want to retrieve the time of item in the list (but actually you don't need it...)
//var typeArguments = prop.PropertyType.GetGenericArguments();
//var listItemType = typeArguments[0];
var lst = Activator.CreateInstance(prop.PropertyType);
prop.SetValue(foo, lst, null);

Categories

Resources