Hi, poking around with EF7 I've found this method missing, and since EF7 provide in almost all cases a strongly typed and loosely typed way of doing things, this is api is missing IMHO.
What about you?
Regards, Max
I would like to submit a PR for this, so IO would like to discuss with the team/community how to implement this since whe only have DbSet
Option 1 is to mimic what IDbSetSource
is doing and return an object.
Option 2 is to create a non generic DbSet.
I personally didn't like option 1 because IMHO returning object from a Set method in DbContext leads to a poor user experience, I didn't like option 2 either because we need to create another empty class (like DbSet
Let me know if there are others options, or which one you prefer.
Regards, Max
Hi @ilmax,
I also look at this issue :confounded: .
My opinion same - its need to create non generic DbSet, but there is a little API difference from DbSet(T)
-> DbSet
.
If you look at DbSet(T)
api you will see that it support for both params T[]
and IEnumerable(T)
input for Range
operations (like in this test and like in this test ).
In case of DbSet(T)
it works since it has more specific IEnumerable(T)
input for these methods.
But in case of DbSet
we need to choose which overload we should expose param object[]
or IEnumerable
. We cannot have same functionality in both DbSet(T)
and DbSet
.
In my opinion we should leave the same api as in EF and skip params input feature (like it was in old EF).
Best regards,
Andrei Tserakhau
I would like to see this in EF7 if possible.
@ilmax @ChangedHourly Could you give some examples of scenarios in which you would use the non-generic context.Set method? This will help drive an appropriate implementation and return type. Keep in mind that in EF7 you never need the DbSet to add, attach, or otherwise manipulate entities because the methods for this are on the context.
Thanks,
Arthur
@ajcvickers Thanks for your info, in fact those methods on DbContext
does satisfy all my actual needs.
For me this issue could be closed!
Thanks, Max
Hi,
I am using the DbContext.Set(Type entityType) to iterate through sets in a context in an export function:
``` C#
public async Task ExportDbSetsAsync(DbContext oldDatabase, DbContext newDatabase, List
{
foreach (DbSetExportInfo setData in dbSets)
{
if (setData.Export == true)
{
var oldSet = oldDatabase.Set(setData.DbSetType);
var newSet = newDatabase.Set(setData.DbSetType);
var progress = new Progress
{
setData.ExportedCount = count;
});
try
{
await ExportDbSetAsync(oldSet, newSet, progress);
await newDatabase.SaveChangesAsync();
}
/* PCVB: Need to send exceptions back to user. More work needed here. */
catch (Exception ex)
{
throw new Exception("Error exporting data: " + setData.DbSetName + "\r\n"
+ ex.Message + "\r\n" + ex.InnerException.Message);
}
}
}
}
public async Task ExportDbSetAsync(DbSet oldSet, DbSet newSet, IProgress
{
if (oldSet.ElementType.Equals(newSet.ElementType))
{
var i = 0;
await oldSet.AsNoTracking().ForEachAsync(entity =>
{
newSet.Add(entity);
if (progress != null)
{
progress.Report(++i);
}
});
}
}
```
I have some lengthy code to create the List
I confess that I haven’t considered yet how else this might be achieved, it may be very straightforward!
FYI I was originally using SqlServerCe, I am using localdb for testing with EF7.
Patrick
From: Arthur Vickers [mailto:[email protected]]
Sent: 13 July 2015 19:39
To: aspnet/EntityFramework [email protected]
Cc: ChangedHourly [email protected]
Subject: Re: [EntityFramework] Expose method DbContext.Set(Type entityType) (#2586)
@ilmax https://github.com/ilmax @ChangedHourly https://github.com/ChangedHourly Could you give some examples of scenarios in which you would use the non-generic context.Set method? This will help drive an appropriate implementation and return type. Keep in mind that in EF7 you never need the DbSet to add, attach, or otherwise manipulate entities because the methods for this are on the context.
Thanks,
Arthur
—
Reply to this email directly or view it on GitHub https://github.com/aspnet/EntityFramework/issues/2586#issuecomment-121018012 . https://github.com/notifications/beacon/ALMjKUrq-eorhN441Rzc-Ev8g20ih95Tks5oc_1DgaJpZM4FVqNi.gif
Clearing back up for triage based on discussion.
@ChangedHourly would something like this work for you?:
C#
var oldSet = (IQueryable<object>)oldDatabase.GetType()
.GetMethod("Set").MakeGenericMethod(setData.DbSetType)
.Invoke(oldDatabase, null);
We think of DbSet mainly as:
LINQ queries have always been hard to write with non-generic DbSets and as @ajcvickers explained, Add()
, Remove()
, etc. can now be called on the DbContext directly.
In my view, the way you are using non-generic DbSet to programmatically bring all the entities from the database into memory is valid but not super common. It is possibly the only scenario left for which the non-generic Set()
can be nicer to use and so I am not sure it is a good idea to have the extra API just for it.
Hi divega,
Thanks for the idea. I think that I may be able to replace the DbSet(Type entityType). I will have a go at testing this shortly.
I understand that you would want to clear out what you consider to be unnecessary code. I have been using EF6 for some time but have been tempted into testing EF7, mainly by the TrackGraph feature.
Patrick
Discussed and concluded that for most scenarios the Add
/Remove
methods on DbContext
are a better option than the non-generic set.
If there are more scenarios like the one @ChangedHourly mentioned then we will reconsider adding it in the future. But we suspect those scenarios are pretty rare and using MakeGenericMethod
is acceptable for those.
I find this decision a serious blow to anyone trying to use ef7 in advances scenarios.
I always used the non generic Set Method in scenarios where I do generic querying on interfaces implemented on the entities and where I only know the entity type at run time. The assumption that you know all your query types at design time is too limiting in my view. I think that this is restricting things on the wrong end.
Any update on this?
@atesoglu @ctonger Can you show some example code of how you are doing the queries after starting with a a non-generic set?
@rowanmiller
We have a scenario where we need to dynamically identify the DbSet to insert data into.
This is for conflict scenarios, where we are storing all the possible conflicts into a different table and later be presented to the user for resolution with an original record against conflicting records.
Is there any alternate way other than a non-generic Set method to do this?
@codinesh For inserts, call Add on the DbContext directly.
Here is my code (works at least for me).
[HttpPost]
public async Task<IActionResult> List(string entityType)
{
var type = typeof(ApplicationUser).Assembly.GetType(entityType);
if (type == null) return NotFound("Unkown entity");
var dbSetMethodInfo = typeof(DbContext).GetMethod("Set");
var dbSet = dbSetMethodInfo.MakeGenericMethod(type).Invoke(_db, null);
var toListMethodInfo = typeof(EntityFrameworkQueryableExtensions).GetMethod("ToListAsync").MakeGenericMethod(type);
var toListMethod = (dynamic)toListMethodInfo.Invoke(dbSet, new []{dbSet, default(CancellationToken)});
var results = await toListMethod;
return Json(results);
}
In my project I should often find entities by some predicate.
I do it this way:
MethodInfo method = typeof(EGeoContext).GetMethod("Set"); MethodInfo generic = method.MakeGenericMethod(type); IEnumerable<IDescription> dbSet = ((IEnumerable)generic.Invoke(context, null)).Cast<IDescription>(); return dbSet.Where(predicate) .Select(o => (String)o.GetType().GetProperty(propName).GetValue(o));
Is there shorter way to do it without non-generic "Set"?
@novikov-alexander That's actually giving you an IEnumerable<IDescription>
, not a DbSet
. The issue is again not how to get the DbSet in a non-generic way, but what you then do with it. There's no way that you can end up with a DbSet<IDescription>
, but you could get to an IQueryable<IDescription>()
by doing this instead:
C#
MethodInfo method = typeof(DbContext).GetMethod("Set");
MethodInfo generic = method.MakeGenericMethod(typeof(Description));
IQueryable<IDescription> queryable = ((IQueryable)generic.Invoke(context, null)).Cast<IDescription>();
@ajcvickers Of course you are right. IQueryable is much better than IEnumerable in this context.
But is this way of getting IQueryable (MakeGenericMethod, Invoke etc) shortest and right?
@novikov-alexander It's a reasonable way to do it.
Most helpful comment
I find this decision a serious blow to anyone trying to use ef7 in advances scenarios.
I always used the non generic Set Method in scenarios where I do generic querying on interfaces implemented on the entities and where I only know the entity type at run time. The assumption that you know all your query types at design time is too limiting in my view. I think that this is restricting things on the wrong end.