I am trying to get a list of all public methods that return an ActionResult from any controller in my solution using reflection but I am experiencing some strange behavior.
Assembly asm = Assembly.GetAssembly(typeof(MyDLL.MvcApplication));
var controllerActionList = asm.GetTypes().ToList();
If I run the above code I get a list of all my types including all my models and controllers etc. just like I would expect. However, if I modify it and run the below code my results list comes back empty. Any idea what's going on here? I would think this should filter the types down so I get a list of all the controllers right?
Assembly asm = Assembly.GetAssembly(typeof(MyDLL.MvcApplication));
var controllerActionList = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type)).ToList();
I got it working by using the code below. I think the direct type comparison is failing for me because I believe I have two different .net versions between these 2 projects.
Assembly asm = Assembly.GetAssembly(typeof(SCCView.MvcApplication));
var controllerActionList = asm.GetTypes()
.Where(type => type.BaseType.Name == "Controller")
.SelectMany(type => type.GetMethods())
.Where(
m => m.IsPublic && m.ReturnType.Name == "ActionResult")
.Select(x => new {Controller = x.DeclaringType.Name, Action = x.Name})
.OrderBy(x => x.Controller).ThenBy(x => x.Action).ToList();
The above will give you a paired list of every public method in a controller that returns an ActionResult
Related
I'm using EF Core 5. I want to make a query and use ProjectTo for mapping my result. But I need to make a filter in nested collection like this:
var questions = await _context
.MedicalDatas
.Include(p => p.MedicalDataItems)
.Include(p => p.MedicalAnswers.Where(x => x.AppUserId == userId))
.ProjectTo<MedicalDataDto>(_cp)
.ToListAsync();
The 2 navigation properties are included in destination type but when I check the SQL generated by EF Core, there is no where clause and it selects all medical answers from the database.
BUT without projecting, it works as expected.
What is wrong here?
you don't need include with projectto
var questions = await _context
.MedicalDatas
.Where(p => p.MedicalAnswers.Any(x => x.AppUserId == userId))
.ProjectTo<MedicalDataDto>(_cp)
.ToListAsync();
We're using MvvmCross in our app, and using the MvxSimpleIoCContainer
In the app startup, we register all of our Migrations.
it's easy do do since all migrations inherit from IMigration
typeof (IMigration)
.Assembly
.CreatableTypes()
.Inherits<IMigration>()
.AsTypes()
.RegisterAsLazySingleton();
After the migrations are registered, we need to run them consecutively, and therefore the MigrationRunner looks a little something like this.
Mvx.Resolve<IMigrationRunner>().RunAll(SystemRole.Client, new List<IMigration>
{
Mvx.IocConstruct<Migration001>(),
Mvx.IocConstruct<Migration002>()
});
as you can see, I'm explicitely constructing each Migration using Mvx. This get's tedious and is prone to mistakes when a bunch of migrations end up in the app.
What I'd prefer to be able to do is resolve the entire collection in one fell swoop, and not have to touch it every time I create a new Migration.
Is there a way to do this via MvvmCross?
Pseudo Code
Mvx.Resolve<IMigrationRunner>()
.RunAll(SystemRole.Client, Mvx.ResolveAll<IMigration>());
I would use LINQ to get the list of types. Unfortunately there's no way to get a list of registered types, so you'll have to enumerate the types again like you do for registration. You can even sort by type name. Now that you have a list of types, you can create a new list of instantiated/resolved types to pass into RunAll(). Something like:
var migrationTypes = typeof (IMigration)
.Assembly
.CreatableTypes()
.Inherits<IMigration>()
.AsTypes()
.OrderBy(t => t.Name)
.ToList();
Mvx.Resolve<IMigrationRunner>()
.RunAll(SystemRole.Client,
migrationTypes.Select(t => Mvx.Resolve(t)).ToList());
This is "browser" code, so no guarantees, but you get the gist.
Ok, so reflection is the answer to this problem for now, and eventually, I'd like to either extend our custom MvxServiceLocator : IServiceLocator to include something like
public IEnumerable<object> GetAllInstances(Type serviceType){...}
but for now I've just got a RunMigrations() method in the app
private void RunMigrations()
{
var migrationType = typeof (IMigration); // IMigration is in a separate assembly
var migrations = GetType().Assembly
.GetTypes()
.Where(
t => migrationType.IsAssignableFrom(t) && !t.IsAbstract)
.OrderBy(t => t.Name)
.Select(m => _serviceLocator.GetInstance(m) as IMigration)
.ToList();
var migrationRunner = new MigrationRunner(Mvx.Resolve<IDbProvider>());
migrationRunner.RunAll(SystemRole.Client, migrations);
}
where _serviceLocator.GetInstance(m) just lives in our custom MvxServiceLocator
public object GetInstance(Type serviceType)
{
return _ioCProvider.Resolve(serviceType);
}
Edit: here's how I extended our service locator wrapper.
public class MvxServiceLocator : IServiceLocator
{
private readonly IMvxIoCProvider _ioCProvider;
public MvxServiceLocator(IMvxIoCProvider ioCProvider)
{
_ioCProvider = ioCProvider;
}
public IEnumerable<TService> GetAllInstances<TService>()
{
var serviceType = typeof(TService);
var registrations = GetType().Assembly
.GetTypes()
.Where(
t => serviceType.IsAssignableFrom(t) && !t.IsAbstract)
.Select(m => (TService)_ioCProvider.Resolve(m));
return registrations;
}
}
I've got a bit of code that tries to access same association path twice and they're really same aliases but because I'm using query objects, I have them in two different places and I am not really sure how to get the alias.
May be some code can clear the confusion:
var privateBlogQuery = new BlogQuery()
.ByUser(1)
.RemoveHidden()
.FetchUserDetails();
//<-------- In Blog Query object class: ------>
/// Gets all the private blogs the user has permissions to view
public BlogQuery ByUser(int userId)
{
var authorisedUsers = null;
this.Left.JoinQueryOver(r => r.AuthorisedUsers, () => authorisedUsers)
.Where(r => r.User.Id == userId);
return this;
}
/// Removes all private blogs user has marked to be hidden
public BlogQuery RemoveHidden()
{
var authorisedUsers = null;
this.Left.JoinQueryOver(r => r.AuthorisedUsers, () => authorisedUsers)
.Where(r => !r.IsHidden);
return this;
}
/// Loads up details of all users who have permission
/// to view the private blog
public BlogQuery FetchUserDetails()
{
var users = null;
var authorisedUsers = null;
this.Left.JoinQueryOver(r => r.AuthorisedUsers, () => authorisedUsers)
.Left.JoinQueryOver(r => r.User, () => users);
return this;
}
There are times when I'm using all 3 criteria individually and the sql generated is precisely what I need and everything is nice and dandy as long as they are used separately.
Now I need to use them all together, at the same time and nhibernate throws an exception duplicate alias and I changed up the alias on these three functions but then I am greeted with the duplicate association path exeception.
A bit of googling and I learnt that it is a bug in hibernate and I also found a few workarounds on this bug
Trouble is I am using Query objects and hence Query over and I am not really sure how to get the association path / alias here.
So how do I go about this please?
make authorisedUsers a membervariable of BlogQuery and use a marker/flag to know if ByUser and RemoveHidden should do the Join
use JoinAlias
example
AuthorisedUser authorisedUser;
bool authorisedUsersJoined;
public BlogQuery RemoveHidden()
{
if (!authorisedUsersJoined)
this.Left.JoinAlias(r => r.AuthorisedUsers, () => authorisedUser);
this.Where(() => !authorisedUser.IsHidden);
return this;
}
FetchUserDetails is mutual exclusive with the other two because filtering on an association prevents NH from initializing the association. You'll need to subquery with the filter and query the resulting Ids and initialize then.
/// Loads up details of all users who have permission
/// to view the private blog
public BlogQuery FetchUserDetails()
{
this.Query = QueryOver.Of<Blog>()
.WhereRestrictionOn(b => b.Id).IsIn(this.Query.Select(b => b.Id))
.Fetch(r => r.AuthorisedUsers).Eager
.ThenFetch(au => au.User).Eager;
return this;
}
I'm trying to get to grips with QueryOver, and I was expecting this to return me a Summary item with its ReportRows collection eagerly loaded.
Update
The first block of code wasn't in my original question but it was the cause of my problem - thanks to dotjoe for the answer
// some code to create a Summary and a whole graph of child records
...
// then...
session.Save(summary);
session.Flush(); // write the changes
session.Evict(summary); // clear out session to check my query below is 'fresh'
// Originally-posted code
Summary summaryAlias = null;
Report reportAlias = null;
var summaryQuery =
session.QueryOver<Summary>(() => summaryAlias)
.Fetch(x => summaryAlias.Reports).Eager
.Left.JoinAlias(() => summaryAlias.Reports, () => reportAlias)
.Where(() => summaryAlias.Id == workItemId);
Summary summary = summaryQuery.SingleOrDefault<Summary>();
session.Close();
However when I hit a breakpoint after session.Close() has been called (to prevent any further lazy loading), I find that everything in my Summary class has been populated, not just the ReportRows collection.
Some examples of things that have been populated even though I wasn't expecting them to be:
ReportRow.Student
ReportRow.Programme
ReportRow.Programme.Modules (a collection)
ReportRow.Programme.Modules.Components (another collection inside each 'Module')
I'm using Fluent automappings, and I've configured it to be lazy-loaded just to be sure using:
.Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Always())
and also tried...
.Conventions.Add(FluentNHibernate.Conventions.Helpers.LazyLoad.Always())
Why is it loading everything?
Thanks
Could you post the generated hbm.xml? example
Also, try this query and see what happens if you access any lazy properties after the using statements...
Summary summary = null;
using(var session = factory.OpenSession())
using(var tx = session.BeginTransaction())
{
summary = session.QueryOver<Summary>()
//.Fetch(x => x.Reports).Eager
.Where(x => x.Id == workItemId)
.SingleOrDefault<Summary>();
tx.Commit();
}
This NHibernate blog entry notes how detached QueryOver queries (analogous to DetachedCriteria) can be created (using QueryOver.Of<T>()). However, looking this over, it doesn't look analogous to me at all.
With DetachedCriteria, I would create my instance and set it up however I need, and afterwards call GetExecutableCriteria() to then assign the session and execute the query. With the "detached" QueryOver, most of the API is unavailable (ie, to add restrictions, joins, ordering, etc...) until I call GetExecutableQueryOver, which requires takes an ISession or IStatelessSession, at which point you are no longer disconnected.
How do you work with detached QueryOver instances?
EDIT:
Actual problem was related to how I'm storing the detached QueryOver instance:
public class CriteriaQuery<T>
{
internal protected QueryOver<T> _QueryOver { get; set; }
public CriteriaQuery()
{
_QueryOver = QueryOver.Of<T>();
}
// Snip
}
It should be a QueryOver<T, T>.
I'm using NHibernate 3.1.0.4000. The following code compiles successfully:
Employee salesRepAlias = null;
var query = QueryOver.Of<Customer>()
.JoinAlias(x => x.SalesRep, () => salesRepAlias)
.Where(x => x.LastName == "Smith")
.Where(() => salesRepAlias.Office.Id == 23)
.OrderBy(x => x.LastName).Asc
.ThenBy(x => x.FirstName).Asc;
return query.GetExecutableQueryOver(session)
.List();
This illustrates using restrictions, joins, and ordering on a detached QueryOver just like you would with a regular one.
Could you please post the code that demonstrates the API features that are unavailable?