I have a class which contains a generic dictionary:
protected Dictionary<K,T> Data { get; set;}
I wish to allow clients of this class to perform LINQ queries without having to return the Dictionary. I had look at AsQueryable() but that doesn't seems to do what I wish.
An example of the code I would like to write would be:
typeRepo.Query().Where( x => x.name == "wire")
It seems like the dictionary is holding a model (T) by its key (K). The code you'll need is:
public IQueryable<T> Query
{
get { return Data.Values.AsQueryable(); }
}
Just add a public property like that:
public IEnumerable<KeyValuePair<K,T>> Query
{
get { return Data.AsEnumerable(); }
}
and it should work like you expect.
Related
I have the same code logic used across different class objects.
For ex:
var matchingTypes = from matchType in order.Contacts
select matchType;
var matchingTypes = from matchType in customer.Contacts
select matchType;
Instead of writing duplicate lines of code, i would like to pass order, customer class names and get Contacts through it, so that the above code will look like (we are using LINQ in our code)
var matchingTypes = from matchType in objectElement.Contacts
select matchType;
The stuff i tried is passed an object parameter
GetData(object objectElement) // method consuming an object parameter.
var objectOrder= objectElement as Orders;
var objectCustomer= objectElement as Customers;
if(objectOrder!=null)
{
objectElement = (Orders) objectOrder; //type
}
if(objectCustomer !=null)
{
objectElement = (Customers) objectCustomer;
}
By doing so, i am repeating my code, which i would like to avoid, any suggestions/ideas? thanks.
I would like to use objectElement and assign only once, so that i can call like this as shown below
var matchingTypes = from matchType in objectElement.Contacts
select matchType;
An interface would be the preferred way to do this, but you could also use dynamic to duck type a method:
public IEnumerable<Contact> GetContacts(dynamic yourObject)
{
return yourObject.Contacts;
}
Note this will not give you a compile error if you call it with something that doesn't have a property called Contacts of type IEnumerable<Contact> but will instead give you a runtime error.
Or you don't even actually need a method, you could just do this:
var matchedTypes = ((dynamic)yourObject).Contacts as IEnumerable<Contact>;
Interfaces would be a safer bet, but are a little tricky with generate entity framework classes. But you can do them becuase they are generated as partial classes. So you can do something like this:
public interface IHaveContacts
{
public IEnumerable<Contact> Contacts { get; }
}
and then:
public partial class Orders : IHaveContacts
{
// should need to do anything since the auto-genrated Contacts property
// will satisfy the interface
}
public partial class Customers : IHaveContacts
{
// ditto
}
And now you can do:
var matchedTypes = ((IHaveContacts)yourObject).Contacts;
Or, if you really, really must (which you don't):
var matchedTypes = from matchType in ((IHaveContacts)yourObject).Contacts
select matchType;
Create an interface IContactsContainer:
public interface IContactsContainer
{
public YourContactType Contacts{get;set;}
}
Then your customer and order classes can implement it:
public class Customers : IContactsContainer
{
public YourContactType Contacts {get;set;}
....
}
public class Orders: IContactsContainer
{
public YourContactType Contacts {get;set;}
....
}
After that in your method you can use:
IContactsContainer objectElement = yourOrderObject;
I have Property and PropertyCompliance entities that look something like this...
public class Property{
public virtual ICollection<PropertyCompliance> ComplianceRecords {get;set;}
}
public class PropertyCompliance{
public virtual Property {get;set;}
public DateTime ComplianceDate {get;set;}
public ComplianceRating ComplianceValue {get;set;} //just an enum
}
In a number of places I need to find the PropertyCompliance row closest to a particular date.
var complianceRating = property.ComplianceRecords.OrderBy(cr=>cr.Date)
.Where(cr=>cr.ComplianceDate< checkDate).FirstOrDefault();
I know I could use an expression as such to filter the ComplianceRecords:
var complianceRating = property.ComplianceRecords.Where(SomeExpression)
.OrderBy(cr=>cr.ComplianceDate).FirstOrDefault()
However this isn't really reducing the amount of repetition as all it's doing is replacing the Where() statement.
Is there a way to apply the expression to the Property to allow this filtering to occur within another expression? Something like:
private static Expression<Func<Property, ComplianceRating>> PropertyComplianceForDate(DateTime checkDate)
{
return p => p.ComplianceRatings
.OrderByDescending(cr => cr.Date)
.First(cr => cr.Date <= checkDate).ComplianceRating;
}
public Expression<Func<Property, bool>> PropertyIsCompliant(DateTime checkDate)
{
return (p) => PropertyComplianceForDate(checkDate) == ComplianceRating.Compliant;
}
With "PropertyComplianceForDate" being an expression that could be translated to SQL to allow the PropertyIsCompliant expression to be used in SQL also.
You could declare an expression at class level and reuse it.
Alternatively you could make a method on your table or an extension method. Extension method (inside a static class) would look something like:
public static PropertyCompliance PropertyIsCompliant(this IEnumerable<PropertyCompliance> complianceRecords, DateTime checkDate) {
return complianceRatings
.OrderByDescending(cr => cr.Date)
.First(cr => cr.Date <= checkDate);
}
Scenario:
I Have a table FooTable when column Foo is varchar(8) NOT NULL and the info in this column is like:
Foo
-----------
13940-00
13940-01
13940-02
13941-00
13941-01
Where the numeric part after the hyphen (-) always have two digits.
Problem:
I'm using Ado.net Entity Framework, and I created a class with 2 static methods to help get first and second part of the number:
public class HelperFoo
{
public static string Prefix(string value) { /* code here */ }
public static string Suffix(string value) { /* code here */ }
}
So now I can do something like this:
context.FooTable.Where(w => HelperFoo.Prefix(w.Foo) == "13940");
But, as you probably already know, this command line throws a NotSupportedException. It's because LINQ don't recognize HelperFoo.Prefix, so it can't convert the expression in SQL.
I can write a block of code in SQL that do the same that my methods of HelperFoo so I can create the SQL to my methos.
Question
Can I create something (class, method, or other) that makes LINQ knows my method when I executed the LINQ code?
EDITED
PS: I need something generic that works like a method or SQL function because I need to get this Prefix and Suffix in scenarios like Select, OrderBy, GroupBy and many others.
You could try creating your own IQueryable filters for the FooTable, a bit like this:
public static class Filters
{
public static IQueryable<FooTable> WithPrefix(this IQueryable<FooTable> item, string prefix)
{
return item.Where(i => i.Foo.StartsWith(prefix));
// note that this should be the same code you have in the
// Prefix() method inside HelperFoo...
}
}
Which you can use like this:
context.FooTable.WithPrefix("13940");
UPDATE: Sadly the second option here does not work:
Another option would be to have the helper methods not return a value but a Predicate<> for FooTable:
public class HelperFoo
{
public static Func<FooTable, bool> Prefix(string value)
{
return (i) => i.Foo.Substring(0, 5) == value;
}
public static Func<FooTable, bool> Suffix(string value) { /* code here */ }
}
And use it like this:
context.FooTable.Where(HelperFoo.Prefix("13940"));
Caveat: I'm not entirely sure the second method would not give you the same problem though.
With the the plethera of awnsers and you stating it needs to be more generic and you need the prefix and suffix avaliable to the Select, OrderBy, & GroupBy keywords, you should have the prefix and suffix in two different fields.
Foo Table
----------
Prefix | Suffix
----------------
10245 | 05
With that, you can query them individually to get what you want:
var resultSet = Db.Foo.Where(x => x.Suffix == "05").OrderBy(x => x.Prefix);
With this you can easily add a read-only property to get a formatted value:
public [partial] class Foo {
//Your other code
public string FormattedValue {
get { return Prefix + "-" + Suffix; }
}
}
You can use .StartsWith to check the prefix:
string prefix = "13940";
var result = context.FooTable.Where(w => w.Foo.StartsWith(prefix + "-"));
and .EndsWith to check the suffix:
string suffix = "02";
var result = context.FooTable.Where(w => w.Foo.EndsWith("-" + suffix));
You could create custom getters in your Foo class:
public class Foo
{
//your code
[NotMapped]
public string Prefix { get { return /*whatever*/; }
}
I think this should work.
You cannot filter your database selection based on a custom function - as you say, it cannot convert this to SQL.
For this specific problem, I could propose you use the StartsWith function, which does work on SQL server
context.FooTable.Where(w => w.Foo.StartsWith("13940"));
Use Microsoft.Linq.Translations.
It would look something like this:
partial class FooTable
{
private static readonly CompiledExpression<FooTable,string> prefixExpression
= DefaultTranslationOf<FooTable>.Property(e => e.Prefix).Is(e => e.Foo.Substring(0, 5));
public string Prefix
{
get { return prefixExpression.Evaluate(this); }
}
}
And queried like:
context.FooTable.Where(w => w.Prefix == "13940").WithTranslations();
Nuget gallery page
Documentation
EDIT: This solution works in Select, GroupBy, OrderBy.
I stumbled trying to use my specification inside a LINQ query. The trouble here is with my specification with params.
Let's fake a simple scenario:
public class Car {
public Guid Id { get; set; }
public string Color { get; set; }
public int UsedPieces { get; set; }
// whatever properties
}
public class Piece {
public Guid Id { get; set; }
public string Color { get; set; }
// whatever properties
}
public static class PieceSpecifications : ISpecification<Piece> {
public static ISpecification<Piece> WithColor(string color) {
return new Specification<Piece>(p => p.Color == color);
}
}
what I'm actually trying to do
// Get accepts ISpecification and returns IQueryable<Car> to force just one call to database
var carWithPieces = _carRepository.Get(CarSpecifications.UsedPiecesGreaterThan(10));
var piecesWithColor = from p in _pieceRepository.Get()
let car = carWithPieces.FirstOrDefault() // entire query will does one call to database
where PieceSpecifications.WithColor(car.Color).IsSatisfiedBy(p) // unfortunately it isn't possible
// where p.Color == car.Color -> it works, but it's not what I want
select p;
I know it's a little bit confusing, but I'm trying to avoid a lot of roundtrips inside my real(big) scenario and I know that actually it's impossible to do using raw LINQ with entity framework. I'm tired to try so many blogs and failed(mine) approaches.
Someone knows some real good approach. There's another way to do that?
Error
System.NotSupportedException: LINQ to Entities does not recognize the
method 'Boolean IsSatisfiedBy(App.Model.Piece)' method, and this
method cannot be translated into a store expression.
UPDATE
Basic Specification Pattern
public class Specification<T> : ISpecification<T> {
private readonly Expression<Func<T, bool>> _predicate;
public Specification(Expression<Func<T, bool>> predicate) {
_predicate = predicate;
}
public Expression<Func<T, bool>> Predicate {
get { return _predicate; }
}
public bool IsSatisfiedBy(T entity) {
return _predicate.Compile().Invoke(entity);
}
}
UPDATE
It's pretty easy neat if I do this
// call to database
var car = _carRepository
.Get(CarSpecifications.UsedPiecesGreaterThan(10))
.FirstOrDefault();
// Whoah! look I'm working, but calling to database again.
var piecesWithColor = _pieceRepository
.Get(PieceSpecifications.WithColor(car.Color))
.ToArray();
Repository
// The Get function inside repository accepts ISpecification<T>.
public IQueryable<T> Get(ISpecification<T> specification) {
return Set.Where(specification.Predicate);
}
You can't compile and invoke expression if you want to use it in LINQ-to-entities query. Try to use Predicate directly because LINQ-to-entities builds expression tree which is evaluated by EF LINQ provider and translated to SQL.
IMHO using specification this way doesn't make sense. LINQ-to-entities query is a composite specification. So either use Linq-to-entities or build your own query language using specification and let your repository translate your query to LINQ query.
Take a look at using AsExpandable extension method.
http://www.albahari.com/nutshell/linqkit.aspx
Maybe make IsSatisfiedBy() and extension method to IQueryable. Here is K. Scott Allen's approach:
http://odetocode.com/Blogs/scott/archive/2012/03/19/avoiding-notsupportedexception-with-iqueryable.aspx
Simplified repeated code for each entity type is
public IList<entity1> GetEntity1(.. query params ..)
{
IQueryable<entity1> query = context.entity1;
query = from refDataType in query
where refDataType.Id > 0
select refDataType;
.
. plus more changes to query same for each entity
.
return query.ToList();
}
I wanted to create a generic function that creates the query, but not sure how to go about it?
ie in the following snippet, How do I code for ReturnAGenericQuery?
public IList<entity1> GetEntity1(.. query params ..)
{
IQueryable<entity1> query = context.entity1;
query = ReturnAGenericQuery of type entity1
return query.ToList();
}
public IList<entity2> GetEntity2(.. query params ..)
{
IQueryable<entity2> query = context.entity2;
query = ReturnAGenericQuery of type entity2
return query.ToList();
}
private IQueryable<T> ReturnAGenericQuery<T> ()
{
return IQueryable of entity1 or entity2
}
Your example is a little vague, but it looks like you need something along the lines of:
private IQueryable<T> ReturnAGenericQuery<T>(IQueryable<T> source)
where T : SomeBaseTypeForAllYourEntities
{
IQueryable<T> result =
from refDataType in source
where refDataType.Id > 0
select refDataType;
// Other stuff here
return result;
}
public IList<Entity1> GetEntity1( ... )
{
return ReturnAGenericQuery(context.entity1).ToList();
}
The reason you need the 'where T :' clause is because T needs to be a type that has a property 'Id' for your LINQ where-clause to work ... so you'd need to derive Entity1 and Entity2 from a base class that defines that property. If you need any other properties for the 'other stuff' these will need to be added to the base class too.
Addendum:
If context.entity1 (whatever collection that refers to) is not an IQueryable<entity1>, then you may need to use context.entity1.AsQueryable() instead.
Originally my query was wrong, it was supposed to query from refDataType in source rather than in result... duh.
Provided you have the right kind of inheritance structure (see below), this compiles fine.
public class SomeBaseTypeForAllYourEntities
{
public int Id { get; set; }
}
sealed public class Entity1 : SomeBaseTypeForAllYourEntities
{
... other properties, etc. ...
}
You need an interface.
private IQueryable<T, R> ReturnAGenericQuery<T> (T entity) where T : IQueryable, IHasRefDataType
{
return from DataType refDataType in entity
where refDataType.Id > 0
select refDataType;
}
struct DataType
{
public int Id;
}
public interface IHasRefDataType
{
DataType refDataType;
}