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;
}
Related
I am indexing apples with their specified properties (such as color) using API Laravel. I use join to retrieve apples which are related to a specified brand. but it does not retrieve apples with their own specified properties which are defined in another DB and models.
public function index(Brand $brand)
{
$apples = Apple::join('brands', 'brand_id', 'brands.id')->where('brand_id', $brand->id)->get();
return returnSuccessfulResponse(
trans('api.response.successful.index'),
Resource::collection($apples)
);
}
Apple model:
public function brand()
{
return $this->belongsTo(Brand::class);
}
public function appleProperties()
{
return $this->hasMany(AppleProperty::class);
}
Resource:
return [
'id' => $this->brand->id,
'name' => $this->brand->name,
'apple-properties' => $this->appleProperties,
];
Route:
Route::apiResource('brands/{brand}/apples', 'AppleController');
It is not retrieving appleProperties. I do not understand that reason!
When you use join() method in your queries, it is recommended to use select() as well, so that is no longer ambiguous which table you referenced to. In your code, the query may be something like this:
$apples = Apple::join('brands', 'brand_id', 'brands.id')->select('apples.*')->where('brand_id', $brand->id)->get();
Simplified Model:
public class GolfCourseDetailsPart : ContentPart<GolfCourseDetailsRecord>
{
public bool ShowInHomePage {... //Get and Set using Retrieve and Store methods
}
Simplified Migrations:
ContentDefinitionManager.AlterTypeDefinition("GolfCourse", gc => gc
//...
.WithPart(typeof(GolfCourseDetailsPart).Name)
);
I need to filter all items of type "GolfCourse" to get only the ones that have ShowInHomePage set to true.
Filter:
I have created a filter implementing the IFilterProvider interface and it returns all the GolfCourse content items but I couldn't get to filter by ShowInHomePage yet:
private void ApplyFilter(FilterContext context)
{
context.Query = context.Query.Join(x=>x.ContentPartRecord(typeof(GolfCourseDetailsRecord)));
}
How could I get to filter by the property ShowInHomePage??
You are almost there, the only part missing is the .Where clause. In a HQL query it looks like this:
private void ApplyFilter(FilterContext context)
{
context.Query = context
.Query
.Join(x => x.ContentPartRecord(typeof(GolfCourseDetailsRecord)))
.Where(x => x.ContentPartRecord<GolfCourseDetailsRecord>(), g => g.Eq("ShowInHomePage", true));
}
Is there any reason you want to create an IFilterProvider?
Those will be only useful if you want to have a customized filter available for query projections.
If you simply want to get filtered data programmatically then I would use Query method of ContentManager.
Here is a set of samples on how querying Orchard, I think it will be more useful for you than if I simply put here the query you need: https://orchardtrainingdemo.codeplex.com/SourceControl/latest#Controllers/ContentsAdminController.cs
I am using Phalcon and have a model Order that has a one-to-many relationship with model OrderAddress. I access those addresses through the following function:
public function getAddresses($params = null) {
return $this->getRelated("addresses", array(
"conditions" => "[OrderAddress].active = 'Y'"
));
}
The OrderAddress model has a public property errors that I do not want persisted to the database. The problem I am having is that everytime I access the getAddresses function, it reloads the object from MySQL which completely wipes the values that I set against that property.
I really only want the OrderAddress models to be loaded once, so that each call to getAddresses doesn't make another trip to the DB- it just iterates over the collection that was already loaded.
Is this possible?
I suppose there's no such option in phalcon, so it has to be implemented in your code.
You could create an additional object property for cached addresses, and return it if it's already been initialized:
protected $cachedAddresses = null;
public function getAddresses($params = null) {
if ($this->cachedAddresses === null) {
$this->cachedAddresses = $this->getRelated("addresses", array(
"conditions" => "[OrderAddress].active = 'Y'"
));
}
return $this->cachedAddresses;
}
This could be a quick solution, but it will be painful to repeat it if you have other relations in your code. So to keep it DRY, you could redefine a 'getRelated' method in base model so it would try to return cached relations, if they already were initialized.
It may look like this:
protected $cachedRelations = [];
public function getRelated($name, $params = [], $useCache = true) {
//generate unique cache object id for current arguments,
//so different 'getRelated' calls will return different results, as expected
$cacheId = md5(serialize([$name, $params]));
if (isset($this->cachedRelations[$cacheId]) && $useCache)
return $this->cachedRelations[$cacheId];
else {
$this->cachedRelations[$cacheId] = parent::getRelated($name, $params);
return $this->cachedRelations[$cacheId];
}
}
Then, you can leave 'getAddresses' method as is, and it will perform only one database query. In case you need to update cached value, pass false as a third parameter.
And, this is completely untested, but even if there're any minor errors, the general logic should be clear.
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;
}
}
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?