Entity Framework, generic List - c#

I have a question about generic list.
Assume that I have a database which contains three tables, User table, Order table and Item table.
In the C# code, I want a function, called GetList to get all he records from one of the three tables.
If I don't use generic method, I have to create 3 different methods which looks like GetAllUsers(), GetAllOrders() and GetAllItems(). Inside these 3 methods, I will have to write the linq to query the database.
My question is how do I implement a generic method.
I want to use EF 4.1. I know how to do it by using NHibrate tho.
UPDATE
What about this? I found it from another post, it can be used to get one single record
public T GetSingleEntity<T>(Expression<Func<T,bool>> expression) where T:class
{
using (PersonalLinksEntities dbContext = new PersonalLinksEntities())
{
return dbContext.CreateObjectSet<T>().SingleOrDefault(expression);
}
}
PersonalLinksEntities is generated by EF, it is database first model.
Here is how I call this method
public ActionResult Index()
{
var thisOne = base.GetSingleEntity<PersonalRecord>(r=>r.RecordID==1);
return View();
}

The specific implementation going to depend on what you are using to access your data. NHib, EF, L2S? These all have some way of accessing an IQueryable generically.
You could expose a method such as:
public IQueryable<T> GetQueryable<T>()
{
//Implementation depends on your data provider
//return dataContext.GetTable<T>();
//return session.Query<T>();
}
But probably what you want to do is follow a repository pattern with a different repository for each type. But you can use an abstract base repository that accepts a type parameter and is reusable.
public abstract class RepositoryBase<T>
{
public IQueryable<T> GetQuery()
{
//Implementation determined by provider
}
}

As there are no details in the question regarding what you have written already. If you just want to GET ALL RECORDS from the tables...Why LINQ...?? You can simply query the database using Data-set and Data-adapter in a method and to make that generic you just pass the table name and get back a Data-set, after that you can play with that Data-set as you like. Please elaborate more if there is any specific complexity in this case which I am missing.

There already exists the method you want on DbContext called Set, e.g. var results = dbContext.Set();
If you are using ObjectContext then the method you want is CreateObjectSet().

Related

EF Core: how to access DBSets programmatically for Seed purposes

In EF Core 3.1+, I would like to seed all DBSets from ANY DbContext with data programmatically with a Seed() extension method. The Seed() method works beautifully in a non-dynamic situation.
For the manual solution, I can add code (that works adding records for each entity) like:
Categories.AddRange(Categories.ToList().Seed(10));
Products.AddRange(Products.ToList().Seed(20));
...
SaveChanges();
My approach for the dynamic solution that would work for any DbContext is code like this:
var entityTypes = myContext.Model.GetEntityTypes();
foreach (var et in entityTypes)
{
myContext.Set(et.ClrType).ToList().Seed();
}
That is referencing a extension method:
public static class Queryables
{
public static IQueryable<object> Set(this DbContext _context, Type t)
{
return (IQueryable<object>)_context.GetType()
.GetMethod("Set")
.MakeGenericMethod(t)
.Invoke(_context, null);
}
}
but returns an error:
System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'
This is for a generic solution using EF 3.1+ where I want to loop through and seed whatever DbSets are in the current context.
I can manually add code (that works adding records for each entity) like:
Categories.AddRange(Categories.ToList().Seed(10));
Products.AddRange(Products.ToList().Seed(20));
...
SaveChanges();
Any idea how to get generic programmatic access to the DBSets so that I can add data to each one?
=== Edit ==========================================================
I updated the extension method as I realized that the duplicate was for multiple methods returned, so I got that part working as below. This code returns the DBSet correctly (sort of):
public static IQueryable<object> Set(this DbContext _context, Type T)
{
return (IQueryable<object>)_context.GetType()
.GetMethods()
.First(p => p.Name == "Set" && p.ContainsGenericParameters)
.MakeGenericMethod(T)
.Invoke(_context, null);
}
Now the code runs without throwing an error, but the item is not added.
Simplifying the code so that the Seed() function is not a factor, I decided to test by just adding a single record as below. I can see the DBSet but still no new record.
// Does no throw an error but does not add the item
dynamic newItem = Activator.CreateInstance(et.ClrType);
var set = myContext.Set(et.ClrType);
set.ToList().Add(newItem); // Simplified from .Seed();
Any ideas on this?
DbContext has multiple Set<T> methods with different parameters.
The easy way to get the right MethodInfo is to create a matching delegate;
var method = new Func<DbSet<object>>(_context.Set<object>).Method.GetGenericMethodDefinition();
method.MakeGenericMethod(T).Invoke( ... );
Of course, if you have defined DbSet properties for each type, you could loop through those.
Your other problem, is set.ToList().Add(.... Calling ToList loads the entire table into a list object, that doesn't know anything about the DbSet it came from. You need to call DbSet<T>.Add, not List<T>.Add.
I would move your entire seed method into a generic method, make that generic for each type, then call it.
private void Seed<T>(DbContext context, T obj) =>
context.Set<T>.Add(obj);
var method = new Action<DbContext, object>(Seed<object>).Method.GetGenericMethodDefinition();
foreach (var et in entityTypes)
method.MakeGenericMethod(et.ClrType).Invoke( ... );
It seemed that this would be a simple thing to do for EF Core but it has not turned out to be so and may not be possible.
From this link, it seems that recent changes to EF have made this harder to do.
If you jump through hoops with reflection using 'DbContent.Set' to get a list of DbSets, you will find these to actually be of 'InternalDbSet' type with a bunch of warnings about how you should not use them. There is no way to cast them to DbSet or an interface such as IDbSet.
But if anyone has a good solution, please post, as I now intrigued to see whether it can be done.

Includes generic type parameters

I'm writing some BLL code to sit on top of Entity framework (DAL classes generated with DBContext but that doesn't matter for this question). Here's one of my routines:
public static Customer Get(int32 CustID, IEnumerable<string> IncludeEntities = null)
{
}
So when I call it I pass a CustID, an optinal list of Entities I want included -such as "Orders", and "OrderDetails":
Customer customer = CustomerBLLRepository.Get("ALFKI",
new[] { "Orders", "Orders.Order_Details"});
It works fine but I don't like calling it with a list or array of strings - I'd like to get strong typing so the IDE can assist.
I could receive a list of types by declaring it like this:
public static void GetTest(Int32 CustID, params Type[] IncludeEntities)
{
}
and get the class name as a string for the includes to work, but then the caller has to use typeofs like this:
CustomerRepository.GetTest(123, typeof(Order), typeof(OrderDetails));
which is not the end of the world, but this causes problems because OrderDetails is actually a navigation property from Orders, and the include needs to be called Orders.OrderDetails, and I'd have to have the code poke around to find which entity OrderDetails in the child of and still generate the string.
What I really want is a strongly typed list of entities to pass as includes in the same format EF wants them as includes but I think I am SOL.
Assuming your EF model has relationships maintained
Why not use a Custom Get Routine that takes a Lambda.
Based on your sample, you get a "customer" back.
public class RepositoryCustomer: RepositoryBase<Customer>
...
...
public class RepositoryEntityBase<T>
public virtual T Get(Expression<Func<T, bool>> predicate)
return Context.Set<T>.Where(predicate).FirstOrDefault();
You the call the Generic Get routine for Any sets on you context,
var aCustomer = RepositoryCustomer.Get(c=>c.id=="ALFKI" && c.Orders.OrderDetail=="bla")
The Virtual navigation properties are very useful and flexible.
Determining which entities to include in the query should not be a task for anything other than the BLL. If the BLL takes a property or multiple properties which refer to the structure of the data store, then you're saying whatever calls the BLL needs to know about the internal structure of the data store. Edit: not being clear enough. It's plain wrong to do this.
IMO you should create a separate method for each use case - otherwise your BLL is just a helper method for the DAL, and has no responsibility to separation of concerns.
It's a major issue with the Entity Framework - MS make it seem as if you should put your queries together at whatever point you like, and use and keep the entities alive wherever you like too. Makes it very difficult to see the light indeed.

Can I allow queried objects to affect the query that fetches them with Linq and NHibernate?

I have a set of subclassed domain objects that I fetch with Linq and NHibernate. Here's an example of what I have:
public abstract class Car {
public abstract bool Runs();
}
public class Junker : Car {
public override bool Runs() {
return false;
}
}
public class NewCar : Car {
public override bool Runs() {
return true;
}
}
What I need to do is to fetch only the cars that Run(). So, I want to do this:
var goodCars = _session.Query<Car>().Where(car => car.Runs());
... but, that doesn't work because Runs() isn't a supported query source. Here's the error I get:
Cannot parse expression 'car' as it has an unsupported type. Only query sources (that is, expressions that implement IEnumerable) and query operators can be parsed.
I've tried separating the query into two steps: 1) get all cars, 2) filter by Runs() ... but I can't do this because it breaks Lazy Loading (my domain model is a bit more complex that my car example). Besides, I only want to fetch the items from the database that actually fit my query.
Is there a way to do what I'm trying to do?
You cannot do this. The thing you're trying to do is impossible to translate into a SQL query and since that is all NHibernate is doing ultimately...no.
To get all with one query is going to require you dropping down to the db level and using some non-domain knowledge. I recommend hiding this behind a service interface.
public interface RunningCars {
IEnumerable All();
}
and implementing it using maybe a custom sql query or a stored procedure.
How does doing it in 2 steps break lazy loading? Perhaps you need to specify that you want to pre-fetch those asociations during the initial query.
Also in this specific example, couldn't you just fetch all instances of NewCar?
if you convert Runs to a read-only property
public virtual bool Runs {get; private set;}
You can map it in your HBM and query the runs property. But being this isn't your actual model, there isn't really a way to help guide you other than to say
change the model
you cannot query object methods

Ordering by custom entity properties in Entity Framework

Just another question about custom extended properties in Entity Framework entities.
Entity model is quite simple in general. Of course in reality it is more complicated, but just for simplyfing I am not pasting all the generated code, just classes and needed properties.
So, I have the entity classes:
partial class Calibration
{
public string Identifier {get;set;}
public Device CalibratedDevice {get;set;}
}
partial class Device
{
public string Name {get;set;}
public ModelGroup ModelGroup {get;set;}
}
partial class ModelGroup
{
public Model[] Models {get;set;}
}
partial class Model
{
public Name {get;set;}
}
And I need to extend Calibration class with additional calculated property in another file.
partial class Calibration
{
public string ModelGroupName {get;set;}
}
This property is calculated like this:
string.Join(" / ", CalibratedDevice.ModelGroup.Models.Select(m => m.Name));
And finally I need to sort ObjectSet of Calibration entities by this calculated property.
Of course, code like
Calibrations.OrderBy(c => c.ModelGroupName)
will not work with throwing an exception, because EF cannot translate ModelGroupName property to database.
Of course, I know the easiest way to do it:
Calibrations.AsEnumerable().OrderBy(c => c.ModelGroupName)
And of course, it doesn't works for me, I don't want to load all the objects from data storage and sort them in linq because I need only a small piece of the whole ordered object set.
I know the approach with storing calculation lambdas instead of properties and passing it to OrderBy method, but it doesn't works either because I have more complex calculation logic than simple a + b.
For example
partial class Calibration
{
public static Expression<Func<Calibration, string>> ModelGroupName
{
get
{
return c => string.Join(" / ", c.CalibratedDevice.ModelGroup.Models.Select(m => m.Name));
}
}
}
Calibrations.OrderBy(Calibration.ModelGroupName)
will throw an Exception because EF cannot thanslate string.Join method to database.
I worked with the first version of EF and this annoying method-translation mechanism was a disaster. And now after few years of EF evolution this problem exists and I can found any suitable solution.
So, please, suggest the ways to organize IQueryable EF sorting by custom properties with calculation logic witch is not directly translated to SQL.
Thanks for replies.
You can map SQL functions to CLR functions in Entity Framework.
Here's a tutorial on how it is done:
http://blogs.microsoft.co.il/blogs/gilf/archive/2009/05/28/entity-sql-user-defined-functions-in-entity-framework-4.aspx
Please let me know if you need further help.
EF converts IQueryable objects into SQL statements that run on a db. You're asking if EF can translate arbitrary C# code into SQL - no, it can't.
It might be possible to construct a query that returns the right result set, which your custom properties can use - it depends on the logic.

How do I return an entity that is a query of multiple tables

I have a web service that uses the Entity Framework for storage and exposes a public API for CRUD operations.
If I have an entity such as User which has a 1 to many relationship with a Car entity, how do I easily return in my web service method of GetUser(int userId) a instance of user that looks like this:
public class User{
string Id;
IEnumberable<Car> Cars;
}
Does this work by default within the entity framework, because I assume that the Cars property is lazy when using it on the server side.
Entity objects are serializable and you will automatically get the Cars property.
However, depending on your LINQ query the Cars property may or may not have been loaded. If you always want to load the Cars property within your given web method, then you should explicitly load the property. Below are a few ways to guarantee you load the Cars property.
1) use the Include("Cars") method in your LINQ query.
2) use projection.
3) Explicitly load the Cars property on an instance of a User object. For example,
userA.Cars.Load()
First of all you should [Include] Cars at the metadata definition of User entity. Also you probably need to rewrite the getUsers() method to fill the cars. Something like this.
public IQueryable<User> GetUsers()
{
return this.ObjectContext.User.Include("Cars");
}
It's not easy to explain shortly, i recommend you to visit this, and this web sites.
Hope helps!
I've used LINQ-To-SQL to achieve a similar result, and for me it "just worked." I'd try it out and see what happens. You may need to explicitly define the [DataContract] on the items.

Categories

Resources