EF custom selector expression - c#

I have the following simple scenario. I have a Customer class, which is defined in my edmx. I have an Order class as well, and a Customer can have zero or more Orders through its Orders property.
I defined an OrderCount property in my Customer class like this:
public partial class Customer
{
public int OrderCount { get; set; }
}
I would like to easily write Select queries, which load the value of this OrderCount appropriately. I tried doing this in the Customer class:
public partial class Customer
{
public int OrderCount { get; set; }
public static Expression<Func<Customer, Customer>> DetailSelector = c =>
{
c.OrderCount = c.Orders.Count;
return c;
};
}
And then the Select query:
var customersWithOrderCount = ctx.Customers.Select(Customer.DetailSelector);
However, this way I get an error, that the lambda with a statement body can not be converted to an Expression.
Then I tried to do this:
public partial class Customer
{
public int OrderCount { get; set; }
public static Expression<Func<Customer, Customer>> DetailSelector = c => Customer.LoadDetail(c);
public static Customer LoadDetail(Customer customer)
{
customer.OrderCount = customer.Orders.Count;
return customer;
}
}
But this way when I execute the Select query I get the following exception:
LINQ to Entities does not recognize the method 'Customer LoadDetail(Customer)' method, and this method cannot be translated into a store expression.
What would be the easiest way to do this?
UPDATE: Just for clarification, I am specifically looking for a way to do this with an Expression. So I do not want to load the Orders from the database, I just want to fill the OrderCount property.
Thanks,

This is a bit simplistic, but I think stays with a basic working design on the entities. The best way IMHO is to use .Order.Count. If you are pulling it from there, why not keep it there?
If you want to write select queries, include the value from Customers.Orders directly and the proper SQL will be generated for you. Otherwise I would personally include this value to be set in a CustomerRepository class right after I load the customer. Then you always get your customer from one place and you know its set (implies usage of the Repository Pattern of course)

Related

How can I create a generic implementation that lists data from my database?

I've got a large number of tables in my database that are essentially supporting data. These tables list nationalities, genders, languages, etc. and are all based on the same data model:
public class SupportDataModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Deleted { get; set; }
}
public class Gender : SupportDataModel
{
}
This data is presented in DropDownList controls mostly so I need to query each table to get a list. Since I don't want to have to rewrite this query every time I need to access the data, I've written it as a helper class:
public class GendersHelper : IAlternateHelper<Gender>
{
public List<Gender> ListItems()
{
using (var db = new ApplicationDbContext())
{
return db.Genders.Where(x => !x.Deleted).ToList();
}
}
}
For each of these classes, this function is identical except in the table it queries. That's why I'd like to write a single class that uses the type that I pass in to it as the determining factor for which table I'm querying, but I don't know how to do this.
Here's what I've got so far...
public abstract class SupportingDataHelper<T>
{
public List<T> ListItems()
{
// Logic to determine which table gets queried,
// as well as the query itself should go here.
}
}
How do I get this method to determine from the type passed in which table to query and then return a list of those items?
You can just use DbContext.Set<T> which returns a set for selected type:
public class SupportDataRepository<T> where T : SupportDataModel
{
public List<T> ListItems()
{
using (var db = new ApplicationDbContext())
{
return db.Set<T>().Where(x => !x.Deleted).ToList();
}
}
}
However, I wouldn't call this class Helper, because it looks more like a repository.
Another thing to consider is that you definitely don't want to create an empty class like:
public class Gender : SupportDataModel
{
}
because it doesn't make much sense. Perhaps, you may want to use enum property to define a type of SupportDataModel. In this case, you will have only one table (with more rows though), one simple class with simple repository class and no inheritance or generics.

Creating common class objects based on class name and use it

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;

Passing generic class name in dbcontext

My entities context file code:
public partial class MyDbContext : DbContext
{
//dbset 1
public DbSet<Customer> Customers { get; set; }
//dbset 2
public DbSet<Order> Orders { get; set; }
}
Class Shipments has a method GetCustomerName
public class Shipments
{
public string GetName(object caller, System.Data.Entity.DbSet objectContext)
{
//This one passes.
IQueryable<Customer> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
//This one fails
IQueryable<caller.GetType()> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
}
}
Question: I would like to remove the hard coding of class Customer there, instead call this method by passing the class name as parameter?
How can I implement this? In this case caller.GetType().Name didn't work for me.
GetName(Order, mydbContext);
GetName(Customer, mydbContext);
Both should work with the same code (trying it to make generic, I am not sure how to cast it generic). Any help will be great. Thanks.
Like so many people, including myself early on, you are not seeing the difference between a data type and an instance of the Type class. A data type is something that is known at compile time. When you call GetType, what you get back is an object whose data type is Type. It contains information about a data type but it is not a data type itself.
What you need to do is make your GetName method generic as well:
public string GetName<T>(T caller, System.Data.Entity.DbSet objectContext)
{
IQueryable<T> myEntity = from p in objectContext
select p where p.Id=1; //get customer name
}
To be able to do that though, the compiler must know that type T actually has an Id property. That means that T has to be constrained to be a specific base class or interface that declares an Id property. In the case of auto-generated EF classes, an interface is your only option.
There are still other issues with your code, but that covers what you are actually asking about.
I don't see the point of passing an object argument just for the sake of getting its type. You can use generics for that:
public string GetName<T>(DbSet objectContext) where T : BaseEntity {
IQueryable<T> myCustomer = from p in objectContext
select p where p.Id = 1;
//Get the name
//Return the name
}
Please note that I added a constraint for T to extend from a BaseEntity.
All your entities should then extend from this abstract class (which should contain the property Id and Name for getting the name).
You would then be able to retrieve the name of, not only Customer entities, but even Order entities:
string customerName = GetName<Customer>(context);
string orderName = GetName<Order>(context);
If only your Customer entity has the property Name, then don't use generics at all, you would be better defining the type explicitly:
public string GetName(context) {
IQueryable<Customer> customer = from p in context
select p where p.Id = 1;
//Get the name
//Return the name
}

Issue with Filtering Entity Framework data by Calculated Fields

Here's a simplified EF Scenerio of my issue:
public partial class MyClass
{
public int ID { get; set; }
public byte Month { get; set; }
public int Year { get; set; }
public DateTime CalculatedDate
{
get
{
return new DateTime(this.Year, this.Month, 1);
}
}
}
I'm using a repository pattern to access these objects, which is also implementing a Where(predicate) method that returns an IEnumrable, just like LINQ. it is being used like this:
var myClasses = myClassRepo.Where(mc=> mc.ID > 10);
this works well and returns the expected objects with all fields, including CalculatedDate.
HOWEVER, when i try to use the calculated field as part of the predicate like this:
var myClasses = myClassRepo.Where(mc=> mc.CalculatedDate == DateTime.Now);
I Receive an error:
Object reference not set to an instance of an object.
I know I can "walkaround" this by retrieving a first set of results, and then filtering it further by the calculated field. but I'm trying to understand why this is happening and what could be done to fix this.
Entity Framework tries to convert your LINQ to SQL, so it has trouble with trying to convert your MyClass.CalculatedDate method into something recognizable in SQL. You may be able to get around this by adding an .AsEnumerable() call in your LINQ before your where, like so:
var myClasses = myClassRepo.AsEnumerable().Where(mc => mc.CalculatedDate == DateTime.Now);
I think it's because EF doesn't support querying on custom properties, as they cannot be translated into a DB column

Best Way To Filtering Child Entities In Entity Framework

I am not deleting entities. I just sign them by IsDeleted property. The problem is when I get a parent element, its all child elements are loaded even if IsDeleted propery is true or false. Then I did something like below, but I want to know is there a better way to do it ?
var result = from p in context.Set<Product>().Include(x => x.Reviews)
select new
{
Product = x,
ProductReviews = x.ProductReviews.Where(y => !y.IsDeleted)
};
var products = new List<Product>();
foreach (var product in result.OrderBy(x => x.Product.Id).Skip(skipRecords).Take(pageSize))
{
var p = new Product();
p = product.Product;
p.ProductReviews = product.ProductReviews.ToList();
products.Add(p);
}
return products;
How to improve this code block ?
Thanks
What I did to address this type of situation before was to create a specific interface signifying the classes that are "flag deleted" like this and then create an Extension Method that filters them out.
If you only have one class with the IsDeleted property, then you don't need a separate interface and you can just use the class instead. But I'll assume for the moment that you do have multiple classes and that the interface is needed.
So the interface would be defined like so:
public interface IHaveIsDeleted
{
public bool IsDeleted { get; set; }
}
Then my Extension Method would be defined in a static class like so:
public static class MyExtensionMethods
{
public IQueryable<T> FilterDeleted(this IQueryable<T> src) where T : IHaveIsDeleted
{
return src.Where(x => !x.IsDeleted);
}
}
Because this is done on an IQueryable<T>, the where clause gets built into the query that is sent to the database, so there won't be any records returned where IsDeleted is true. So all you would have to do in your example is call x.ProductReviews.FilterDeleted().
Now, the project that I was using this method in was actually using LINQ2SQL. And I'm rather new to EF myself, so there might be a more 'EF specific' way of doing this, (like perhaps some kind of Type Per Hierarchy construct, maybe?), but I just thought this would at least help make your queries simpler.
Hope that helps! ;)

Categories