New Posts New Posts RSS Feed: BOSFetch - How to have DevForce find it...
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

BOSFetch - How to have DevForce find it...

 Post Reply Post Reply
Author
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Topic: BOSFetch - How to have DevForce find it...
    Posted: 23-May-2009 at 1:42am
I have implemented a BOF class as follows, but no matter what I set I cannot get this detected by the probe, and the log error is always:
IdeaBlade.EntityModel.Server EntityServer::GetInstanceImplementing Unable to locate an implementation of: IdeaBlade.EntityModel.IEntityServerFetching

I've even moved this code into a new assembly, included that reference in the web project, added to the <probeAssemblyNames> in web.config (and checked it is picked up by renaming it and I get an error), yet I always get this report in the log file.

Does the class require a specific namespace, must it be static, or what?

Can you please post a working example project so I (and presumably others) can work out how to implement this?

Cheers,
 Jason


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using IdeaBlade.EntityModel;
using System.Diagnostics;
using System.Linq.Expressions;

public class BOSFetch : IEntityServerFetching
{
    // EXAMPLE:
    //void OnFetching(object sender, EntityFetchingEventArgs e)
    //{
    //    if (e.Query.ReturnType == typeof(Product))
    //    {
    //        int wholesalerid = GetWhilesalerId(HttpContext.Current.User.Identity.Name);
    //        iRisk.DataModel.iRiskEntities de = new iRisk.DataModel.iRiskEntities();
    //        e.Query.Filter = new Filter(p => p.Wholesaler.WholesalerID == wholesalerid);
    //    }
    //}

    #region IEntityServerFetching Members

    public void OnFetching(EntityServerFetchingEventArgs args)
    {
        Debug.WriteLine("OnFetching - " + args.Query.ToString());
        //((EntityQuery)args.Query).Filter(
        ////args.Principal.Identity.Name
        //if (args.Query.ReturnType == typeof(Portfolio))
        //{
        //    string uname = args.Principal.Identity.Name;
        //    //var qry = ((IEntityQueryable<Portfolio>)args.Query).Where(p => true); //.Where(p => p.Client.Username == uname);
        //    //args.Query = (qry as IEntityQuery<Portfolio>);
        //}
    }

    #endregion

}

Back to Top
davidklitzke View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 14-Jun-2007
Posts: 715
Post Options Post Options   Quote davidklitzke Quote  Post ReplyReply Direct Link To This Post Posted: 23-May-2009 at 7:03pm

 The reason your BOF class can not be found could be because you are not using a valid probeAssemblyName in your config file. For Silverlight applications, the probeAssemblyNames must be fully qualified.

For example. you might need a probeassemblyname like this:

       <probeAssemblyName name="CaseManager.DomainModel.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
 
Also check your reference for your probeAssemblyName by looking at the Path in the Properties window and make sure that the path is correct.
Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 23-May-2009 at 8:04pm
Since this class is in the main application, why would it require a fully qualified name and version?
Adding the version in this way confuses the DevForce compile project and it starts modifying the web.config file, duplicating entries:
 

<probeAssemblyNames>

<probeAssemblyName name="TestSilverlightApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

<probeAssemblyName name="TestSilverlightApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

<probeAssemblyName name="TestSilverlightApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

<probeAssemblyName name="TestSilverlightApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

etc... (This doubles in length every compile!)
</probeAssemblyNames>
 
The function is defined in the base class of the project, not an external one. The Assembly name is "TestSilverlightApplication", and the BOSfetch.cs code has no namespace defined and is marked as 'Compile'.
I've decompiled the library and the function is there at the top namespace level with the interface defined correctly.
If I add the library as an external DLL then the system loads it and does recognise it, but still fails to find the reference.  I assume it's finding the external reference because if the filename is changed the log shows an error.
Whichever of these method I use, the log still shows the same failure to find that signature.
 
As I said, if someone can post a simple template project showing this feature working then I can use that as a base to start from, then perhaps I can reverse-engineer the process to find why my solution doesn't work.
 
Cheers,
 Jason
Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 23-May-2009 at 8:07pm
Also, I should add that while this is being used by a Silverlight client, isn't it the web service in IIS that implements the BOS?  Is there a difference between the SL and non-SL implementations of the BOS that might be affecting this?
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 23-May-2009 at 10:36pm
Probing for an IEntityServerFetching implementation uses only the top level <probeAssemblyNames> in the ideablade.configuration section, not the probes defined for an EdmKey.  There are no other requirements for the implementation - it does not have to be a static class, in a specific namespace, etc.  The class just needs to implement the interface, and be found within one of the non-EdmKey probe assemblies in the web.config.   You can use the Config Editor if you're unsure of the elements available and their locations.

We plan to include a tutorial showing implementation of IEntityServerFetching, along with other "server side" interfaces, within the next few releases.  The next release will also include an IEntityServerFetching example as part of a Silverlight ASP.NET security sample, but as you'll soon see, there's really nothing complicated about this interface.


Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 24-May-2009 at 3:14am
That's it!
Thanks Kim.

Now that I've wasted about 6 hours chasing this issue, can I suggest that you either put a placeholder in the templated web.config for the probeAssemblyNames tag, or make it clear in the doco that you have reused the same name of child tags in different places?
This base probeAssemblyNames section is mentioned only once on page 408, and even that is not in relation to this usage!!

Now that it's finally working for me, can you explain something about the use of Filter in the OnFetching event?
I have the following:

Accessing the args' EntityManager doesn't work because 'dom' is always null, so what method am I expected to use to get the current EntityManager? The samples keep mentioning _em1, but how would this ever be populated?


    public void OnFetching(EntityServerFetchingEventArgs args)
    {
        //args.Principal.Identity.Name
        if (args.Query.ReturnType == typeof(dimStation))
        {
            //*** Fails with "Type conflict:  the DefaultManager is currently of type EntityManager"
            var dom = DomainModelEntityManager.DefaultManager;
            //*** Fails because dom is always null
            var dom = (DomainModelEntityManager)args.Query.EntityManager;
            //*** works, but seems quite an overhead if an EntityManager already exists that can be reused
            var dom = new DomainModel.DomainModelEntityManager();

            args.Query = ((EntityQuery)args.Query).Filter(
                (IQueryable<dimStation> stat) =>
                    (from s in dom.dimStations join c in dom.factCoverages on s.StationId equals c.StationId where c.PostcodePrefix=="NE" select s).AsQueryable());
        }
    }

Cheers,
 Jason


Edited by jsobell - 24-May-2009 at 3:47am
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 24-May-2009 at 8:51am
Hmm, I need to see why we don't provide an EntityManager as an argument to the method.

This should work:
   var em = EntityManager.DefaultManager;
   var dom = new DomainModelEntityManager(em);

Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 25-May-2009 at 4:26am
Also, how do I access the SessionBundle at the server end?

All I want to do is check in the OnFetching event to see if the current user has the appropriate permissions, by modifying the query to join it to a permissions table and add a "Where tblPermissions.Username=" & args.Pricipal.Name (this is pseudocode - no need to point out the errors) filter.

e.g.:

    public void OnFetching(EntityServerFetchingEventArgs args)
    {
        if (args.Query.ReturnType == typeof(dimStation))
        {

            var em = EntityManager.DefaultManager;
            var dom = new DomainModelEntityManager(em);

            args.Query = ((EntityQuery)args.Query).Filter(
                (IQueryable<dimStation> stat) =>
                    (from s in dom.dimStations join p in dom.permissions on s.StationId equals p.StationId where p.Username==args.Principal.Name select s).AsQueryable());
          }
     }

This does a few odd things:
The EntityManager used for the original query is not available (as you already commented on)
The database is accessed twice, for some reason running a second query returning all the fields in the permissions table (which are never requested by my code).
I would expect the executed code to change from
"Select StationId,Station From dimStations"
to
"Select StationId,Station From dimStations JOIN permissions ON dimStation.StationId=permissions.StationId AND permissions.Username=@1"
@1='Jason'
(or something similar)

Have I missed something here, or am I approaching the Filter method the wrong way? Could this be caused by the fact I'm creating a new EntitytManager?

By the way, typo in the doco - all references to "ILoginManager" should be "IEntityLoginManager"

Cheers,
 Jason
Back to Top
jsobell View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Apr-2009
Location: Australia
Posts: 80
Post Options Post Options   Quote jsobell Quote  Post ReplyReply Direct Link To This Post Posted: 25-May-2009 at 7:09am
Ah, I suspect the second SQL was a lazy load of a joined table row, as the following generates the SQL I would expect:

            args.Query = ((EntityQuery)args.Query).Filter(
                (IQueryable<dimStation> stat) =>
                    (from s in stat join p in dom.permissions on s.StationId equals p.StationId where p.Username == args.Principal.Identity.Name select s).AsQueryable());
Returns:
SELECT
[Extent1].[StationId] AS [StationId],
[Extent1].[Station] AS [Station]
FROM  [dbo].[dimStation] AS [Extent1]
INNER JOIN [dbo].[permissions] AS [Extent2] ON ([Extent1].[StationId] = [Extent2].[StationId]) OR (([Extent1].[StationId] IS NULL) AND ([Extent2].[StationId] IS NULL))
WHERE (N'Jason' = [Extent2].[Username]) AND ([Extent1].[StationId] < 100)

Perfect!
I'd still like to know how to use the SessionBundle though. Can I add data to it? I'd like to store an integer UserId somewhere in 'session' for these calls (instead of Username). Is this the right place?

Cheers,
 Jason
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 25-May-2009 at 10:37am
The SessionBundle is intended for internal use within the DevForce framework.  It's returned by a Login, and then passed with each subsequent request to the server, to both validate the request and re-authenticate the credentials when required.  It's not currently extensible.

What you can do is implement a custom IPrincipal/IIdentity containing the additional properties you want, and then a custom IEntityLoginManager to return the new type.  Your custom IPrincipal will then be available to both client and server code for authorization checks.  Implementing these custom types is really quite simple, although I don't know that we have any samples yet.  The DevForce Help reference does contain an example for the IEntityLoginManager class.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down