Using the logic from one lambda within a second lambda - c#

I've looked at the other questions around this and I just can't work out how to apply the answers to my particular situation. Say you have a couple of models that look like this:
public class Person
{
public int PersonId { get; set; }
}
public class Business
{
public int BusinessId { get; set; }
}
I want to be able to write a couple of different generic methods: one that gets the models using a provided Lambda that might look something like this:
GetWhere(p => p.PersonId == 1)
And one to get the models using a unique key - to make this flexible, I'd like to be able to specify the unique key using a Lambda:
GetByUniqueKey(p => p.PersonId, 1)
Or
GetByUniqueKey(b => b.BusinessId, 1)
Ideally GetByUniqueKey would just be a shorthand method to build up an expression to send to GetWhere, and then return the FirstOrDefault() result. But the logic to do this is completely escaping me. What I want to do:
public IEnumerable<TModel> GetWhere(Expression<Func<TModel, bool>> whereExpression)
{
// Get from DB using expression provided
}
public TModel GetByUniqueKey<TUniqueKey>(
Expression<Func<TModel, TUniqueKey>> uniqueKeyProperty,
TUniqueKey value)
{
return GetWhere(m => uniqueKeyProperty(m) == value).FirstOrDefault();
}
So I want to take the uniqueKeyProperty expression, invoke it on the supplied parameter somehow to get the property, and then use that property in the whereExpression expression.
A note on duplicate questions:
I know this looks like a duplicate of other similar questions, but please note I have read those and I just can't figure out how to apply those answers to my specific use case.
Some clarification in response to comments:
Put simply, I want to do the following:
I want to take the Expression p => p.PersonId and the value 1, and generate a whereExpression that looks like this p => p.PersonId == 1. (Thanks #Rob)

You can build a new expression from the key selector and value provided like so:
public TModel GetByUniqueKey<TUniqueKey>(
Expression<Func<TModel, TUniqueKey>> uniqueKeySelector,
TUniqueKey value)
{
return GetWhere(Expression.Lambda<Func<TModel,bool>>(
Expression.MakeBinary(
ExpressionType.Equal,
uniqueKeySelector.Body,
Expression.Constant(value, typeof(TUniqueKey))),
uniqueKeySelector.Parameters));
}
For querying by ID I wouldn't bother with this approach. Check out the other static methods on the Expression class.

As apparently you would like to implement some kind of polymorphism, one possibility would be to have Person and Business inherit from the same base class or inherit from the same interface and share an Id property. You could define
public class Identifiable
{
public int Id { get; set; }
}
and make Person and Business inherit from in. Then the callback
Func<Identifiable,bool> = iIdentifiable => iIdentifiable.Id == 1
could be called for objects of both classes. However, the original classes would have to be changed for that approach to work.

Related

Generic list/sublist handling

Let's say we have a class
class ComplexCls
{
public int Fld1;
public string Fld2;
//could be more fields
}
class Cls
{
public int SomeField;
}
and then some code
class ComplexClsList: List<ComplexCls>;
ComplexClsList myComplexList;
// fill myComplexList
// same for Cls
class ClsList : List<Cls>;
ClsList myClsList;
We want to populate myClsList from myComplexList, something like (pseudocode):
foreach Complexitem in myComplexList
{
Cls ClsItem = new Cls();
ClsItem.SomeField = ComplexItem.Fld1;
}
The code to do this is easy and will be put in some method in myClsList.
However I'd like to design this as generic as possible, for generic ComplexCls.
Note that the exact ComplexCls is known at the moment of using this code, only the algorithm shd be generic.
I know it can be done using (direct) reflection but is there other solution?
Let me know if the question is not clear enough. (probably isn't).
[EDIT] Basically, what I need is this: having myClsList, I need to specify a DataSource (ComplexClsList) and a field from that DataSource (Fld1) that will be used to populate my SomeField
This is just a mapping, so use some simple LINQ:
ClsList myClsList = new ClsList();
myClsList.AddRange(
myComplexList.Select(Complexitem => new Cls { SomeField = Complexitem.Fld1 })
);
Okay, the easier version assuming we have a known target field on a class (I've written this as an extension method, no need to do
public IEnumerable<Cls> MapField<TSource>(IEnumerable<TSource> sourceList,
Func<TSource, int> sourceSelector)
{
return sourceList.Select(x => new Cls {SomeField = sourceSelector(x)});
}
Called this way
IEnumerable<Cls> result = MapField(myComplexList, x => x.Fld1);
Aside: Since your myComplexList of type ComplexClsList inherits from List (which implements IEnumerable this will work. The result isn't of type ClsList that you wanted, but you could easily call .ToList() on the result and provide a constructor on ClsList that takes a List<Cls>.
And the more complicated version for when we don't know the target field (or type)...
public IEnumerable<TResult> MapField<TSource, TResult, TMap>(
IEnumerable<TSource> sourceList,
Func<TSource, TMap> sourceSelector,
Func<TMap, TResult> resultCreator)
{
return sourceList.Select(x => resultCreator(sourceSelector(x)));
}
Not as pretty to call....
IEnumerable<Cls> result = MapField(
myComplexList,
source => source.Fld1,
valueToMap => new Cls() {SomeField = valueToMap});
Might be a better way, but it's not occurring to me at the moment.
Edit: Actually, you could combine the two Func on the last one into a single one that takes a TSource and creates and maps the necessary fields to TResult, but I'm really not sure what you're gaining with that extra layer of abstraction...
You may want to reconsider extending List classes in the first place. What does inheritance give you, in this case? I suspect that you'll be better off favoring composition over inheritance here. One possible approach would be:
// If you would say that a ComplexCls "is a" Cls, then maybe your inheritance
// relationship belongs here instead.
public class ComplexCls : Cls {
}
public class ClsList
{
public IReadOnlyCollection<Cls> Items {get;set;}
}
public class ComplexClsList
{
public IReadOnlyCollection<ComplexCls> Items {get;set;}
}
Then you can create a ClsClist easily.
ClsList basicList = new ClsList{Items = complexList.Items};
But you may want to take it a step farther and question why the ClsList and ComplexClsList classes exist at all. Why not simply pass around Lists directly. I mean, what's the difference between a ClsList and a "List of Clses" (List<Cls>)?

Linq Expressions: Create a max query using a generic DbSet

In order to simplify creating fake data for unit testing, I want to have a function that can create a generic Entity (which I use as an underlying object for a lot of other classes). The entity has an index, and, for generating a new index, I want to simply find the currently highest number and add one.
I am using Index for a lot of other classes, and would like to make this function as generic as possible. My problem is that I don't know how to specify what DbSet to use in my generic function GetMaxID.
This is what I got so far:
private Entity CreateGenericEntity()
{
return new Entity()
{
Guid = Guid.NewGuid(),
Index = GetMaxID<Entity>(x => x.Index) + 1,
};
}
private int GetMaxID<TEntity>(Expression<Func<TEntity, int>> expression)
{
return _repository.Set<TEntity>().Max(expression);
}
_repository has a bunch of different IDbSets properties, such as
public IDbSet<Customers> Customers{ get; set; }
public IDbSet<Orders> Orders{ get; set; }
etc.
I found that I was missing the declaration of TEntity, which was fixed by appending where TEntity : class to my function. I also had to change the expression to accept int? in order to handle the case where the query returns a null value. The complete function looks like this (if anyone is interested)
private int GetMaxID<TEntity>(Expression<Func<TEntity, int?>> expression) where TEntity : class
{
return _repository.Set<TEntity>().Max(expression) ?? 0;
}

Pass in an Expression to linq's Select

This is linq-to-sql
I have a lot of different classes all doing the same query, but projecting the results slightly differently. Ideally I'd like to be able to have the query in one place, and have the projection passed into the Select method. It works fine for concrete types:
public void GetResults() {
var junk = db.SiteProducts.Select(Project());
}
public Expression<Func<DbEntities.SiteProduct, string>> Project() {
return p => p.ProductAlert;
}
But when I try to return an anonymous type, it fails
public void GetResults() {
var junk = db.SiteProducts.Select(Project());
}
public Expression<Func<DbEntities.SiteProduct, TResult>> Project<TResult>() {
return p => new { p.ProductAlert };
}
I fully understand why generic type inference is failing in the second case. But is there a trick—short of crafting my own Expressions from the ground up—I'm missing that could get this to work?
This is an intriguing question. I think a DTO can help you out here, but there are limitations and pitfalls to watch out for. Take the following LINQPad Example:
class ProjectDTO
{
public string Name { get; set; }
public static Expression<Func<Project, ProjectDTO>> ToDTO = (e) => new ProjectDTO
{
Name = e.Name
};
public ProjectDTO() {}
public ProjectDTO(Project project)
{
Name = project.Name;
}
}
void Main()
{
Projects.Select(p => p.Name).Dump();
Projects.Select(ProjectDTO.ToDTO).Dump();
Projects.Select(p => new ProjectDTO(p)).Dump();
}
SQL Generated:
SELECT [t0].[Name]
FROM [Project] AS [t0]
GO
SELECT [t0].[Name]
FROM [Project] AS [t0]
GO
SELECT [t0].[ProjectId], [t0].[Name], [t0].[Description], [t0].[DateCreated], [t0].[DateModified], [t0].[DateComplete], [t0].[CreatedBy]
FROM [Project] AS [t0]
As you can see, you cannot use a copy-constructor to assign the properties of the DTO as this forces the entire object to be pulled back from the database.
This also slightly limiting if you wanted to extend the base DTO and add more properties for more specialised views of the data, which means you could end up with multiple Expression's with similar code.
However, I quite like option two, but i'm sure this option is quite likely restricted to single type projections, consider the following example:
var query = from p in Projects
join t in Tasks on p.ProjectId equals t.ProjectId
select ProjectDTO.ToDTO; //Can't be used like this
I don't think you can use the Expression in this type of query-syntax. Generally speaking, I don't think there will be a solution that works across the board. You may have to review your design to see if you can provide less projections, based on some of the properties being very cheap to always include in the query?
Without using the Dynamic LINQ library or building the expression tree manually, I would also like to see if it is possible with LINQ-SQL/LINQ-Entities to create dynamic selects.
If I understand your question correctly you can use this code:
first declare a method for selecting your data like this:
public List<TResult> FindAll<TResult>(Func<Regions, TResult> selector) where TResult : class
{
using (RepositoryDataContext = new DataClasses1DataContext())
{
return RepositoryDataContext.Regions.Select<Regions, TResult>(selector).ToList<TResult>();
}
}
then you can build your select statement like this:
Func<Regions, SelectAllRegion> select = r => new SelectAllRegion
{
RegionID = r.RegionID,
RegionDescription = r.RegionDescription
};
my SelectAllRegion :
public class SelectAllRegion
{
public SelectAllRegion()
{
}
public int RegionID { get; set; }
public string RegionDescription { get; set; }
}
and region is Region table in northwing.I hope this help you
IdeaBlade has a ProjectionSelector class that you can use to abstract your projections. When you need to construct a projection query but you don't know the types involved at compile time, you can create an instance of the ProjectionSelector class and pass in the type information at runtime.
The class, and sample code, can be found here:
Create dynamic "Select", "SelectMany" and "GroupBy" clauses
http://drc.ideablade.com/xwiki/bin/view/Documentation/dynamic-projection
This will not work at compile-time. Using dynamic stuff you can make it work of course.
A simple solution is not to use an anonymous type but a custom-made DTO class. Such a DTO class only takes very few lines and is easy to maintain. Usually this is a good solution.

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...

LinqToSql and abstract base classes

I have some linq entities that inherit something like this:
public abstract class EntityBase { public int Identifier { get; } }
public interface IDeviceEntity { int DeviceId { get; set; } }
public abstract class DeviceEntityBase : EntityBase, IDeviceEntity
{
public abstract int DeviceId { get; set; }
}
public partial class ActualLinqGeneratedEntity : DeviceEntityBase
{
}
In a generic method I am querying DeviceEnityBase derived entities with:
return unitOfWork.GetRepository<TEntity>().FindOne(x => x.DeviceId == evt.DeviceId);
where TEntity has a contraint that is it a DeviceEntityBase. This query is always failing with an InvalidOperationException with the message "Class member DeviceEntityBase.DeviceId is unmapped". Even if I add some mapping info in the abstract base class with
[Column(Storage = "_DeviceId", DbType = "Int", Name = "DeviceId", IsDbGenerated = false, UpdateCheck = UpdateCheck.Never)]
Wow, looks like for once I may be able to one-up #MarcGravell!
I had the same problem, then I discovered this answer, which solved the problem for me!
In your case, you would say:
return unitOfWork.GetRepository<TEntity>().Select(x => x).FindOne(x => x.DeviceId == evt.DeviceId);
and Bob's your uncle!
LINQ-to-SQL has some support for inheritance via a discriminator (here, here), but you can only query on classes that are defined in the LINQ model - i.e. data classes themselves, and (more perhaps importantly for this example) the query itself must be phrased in terms of data classes: although TEntity is a data class, it knows that the property here is declared on the entity base.
One option might be dynamic expressions; it the classes themselves declared the property (i.e. lose the base class, but keep the interface) - but this isn't trivial.
The Expression work would be something like below, noting that you might want to either pass in the string as an argument, or obtain the primary key via reflection (if it is attributed):
static Expression<Func<T, bool>> BuildWhere<T>(int deviceId) {
var id = Expression.Constant(deviceId, typeof(int));
var arg = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(arg, "DeviceId");
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(prop, id), arg);
}
This kind of heirarchial mapping isnot possible with LinqToSql. The the mapping is setup it cannot map to properties in base classes. I went around on this for a couple of months when it first came out. The best solution is to use the entity framework. It gives you much more flexibility with creating your object model. It will allow you to do exactly what your trying to do here.
Here is some information on the entity framework: MSDN Article
Try .OfType<>() as posted here https://stackoverflow.com/a/17734469/3936440, it works for me having the exact same issue.

Categories

Resources