This is a migrated thread and some comments may be shown as answers.

Multiple modules with adjustForDynamicLoad

7 Answers 101 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Filipe Peixinho
Top achievements
Rank 1
Filipe Peixinho asked on 07 Sep 2010, 10:47 AM
Greetings.

I have a module in my webApplication that uses ORM. In that module, i have made some alterations to objectScopeProvider.cs in order to use de connection string specified in the file connectionStrings.config of my web.

What i have done in order to aquire this cenĂ¡rio, was to use the method adjustForDynamicLoad() changing it to my app reality.


It was everything going as expected untill i add other module using ORM. In this new module, i have done de same procedure reusing the code of the method adjustForDynamicLoad() from my previous module. After the addition of this module with the adjustForDynamicLoad(), the first one stoped to work and im receiving the same exception in all oql querys.

Exception: No class found for extent "MyClassExtent"

If i dont use the adjustForDybamicLoad in the second module, it all works perfectly.

Can you help me getting de adjustForDynamicLoad() working on the two modules?

7 Answers, 1 is accepted

Sort by
0
Alexander
Telerik team
answered on 09 Sep 2010, 02:57 PM
Hi Filipe Peixinho,

This exception usually means that the database has been opened with metadata from another model. Do the two modules connect to the same database? If so, the metadata from the two models should be merged before calling Database.Get(). If this is the case, please let me know and I will help you to achieve that.
Nevertheless, it would be helpful if you can provide the code of the AdjustForDynamicLoad methods from the two modules as well as the code that invokes those methods.

All the best,
Alexander
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Filipe Peixinho
Top achievements
Rank 1
answered on 14 Sep 2010, 02:14 PM
Hi,
 
I'm really using two different modules that connect to the same database.
 
As for the code used in the method adjustForDynamicLoad, in both modules is the same.
The only diference is the namespace :)
 
Can you help-me solving this problem?
 
A little Below is the code i use.



using Telerik.OpenAccess;
using Telerik.OpenAccess.Util;
 
namespace app.dal
{
    /// <summary>
    /// This class provides an object context for connected database access.
    /// </summary>
    /// <remarks>
    /// This class can be used to obtain an IObjectScope instance required for a connected database
    /// access.
    /// </remarks>
    public class ObjectScopeProvider1 : IObjectScopeProvider
    {
        private Database myDatabase;
        private IObjectScope myScope;
  
         
  
        static private ObjectScopeProvider1 theObjectScopeProvider1;
  
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <remarks></remarks>
        public ObjectScopeProvider1()
        {
        }
  
        /// <summary>
        /// Adjusts for dynamic loading when no entry assembly is available/configurable.
        /// </summary>
        /// <remarks>
        /// When dynamic loading is used, the configuration path from the
        /// applications entry assembly to the connection setting might be broken.
        /// This method makes up the necessary configuration entries.
        /// </remarks>
        static public void AdjustForDynamicLoad()
        {
  
            if( theObjectScopeProvider1 == null )
                theObjectScopeProvider1 = new ObjectScopeProvider1();
              
            if( theObjectScopeProvider1.myDatabase == null )
            {
  
                string myConnString = System.Configuration.ConfigurationManager.ConnectionStrings["LMS_BD"].ConnectionString;
                string xmlAppendConnection;
  
                if (myConnString.ToUpper().Contains("Persist Security Info".ToUpper()))
                {
                    xmlAppendConnection = string.Format("<connectionString>{0}</connectionString>",myConnString);
                }
                else
                {
                    xmlAppendConnection = string.Format("<connectionString>Persist Security Info=True;{0}</connectionString>",myConnString);
                }
                 
                string assumedInitialConfiguration =
                           "<openaccess>" +
                                "<references>" +
                                   "<reference assemblyname='PLACEHOLDER' configrequired='True'/>" +
                                "</references>" +
                                 "<connections>" +
                                  @"<connection id=""LMS_BD"">" +
  
                                  xmlAppendConnection +
  
                                   "<backendconfigurationname>mssqlConfiguration</backendconfigurationname>" +
                                  "</connection>" +
                                 "</connections>" +
                            "</openaccess>";
         
                  
                System.Reflection.Assembly dll = theObjectScopeProvider1.GetType().Assembly;
                assumedInitialConfiguration = assumedInitialConfiguration.Replace(
                                                    "PLACEHOLDER", dll.GetName().Name);
                System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
                xmlDoc.LoadXml(assumedInitialConfiguration);
  
                Database db = Telerik.OpenAccess.Database.Get("LMS_BD",
                                            xmlDoc.DocumentElement,
                                            new System.Reflection.Assembly[] { dll } );
  
                theObjectScopeProvider1.myDatabase = db;
            }
        }
  
        /// <summary>
        /// Returns the instance of Database for the connectionId
        /// specified in the Enable Project Wizard.
        /// </summary>
        /// <returns>Instance of Database.</returns>
        /// <remarks></remarks>
        static public Database Database()
        {
            if( theObjectScopeProvider1 == null )
                theObjectScopeProvider1 = new ObjectScopeProvider1();
  
            AdjustForDynamicLoad();
  
            if( theObjectScopeProvider1.myDatabase == null )
                theObjectScopeProvider1.myDatabase = Telerik.OpenAccess.Database.Get("UpdateDatabaseConnection");
                  
  
            return theObjectScopeProvider1.myDatabase;
        }
  
        /// <summary>
        /// Returns the instance of ObjectScope for the application.
        /// </summary>
        /// <returns>Instance of IObjectScope.</returns>
        /// <remarks></remarks>
        static public IObjectScope ObjectScope()
        {
            Database();
  
            if( theObjectScopeProvider1.myScope == null )
                theObjectScopeProvider1.myScope = GetNewObjectScope();
  
            return theObjectScopeProvider1.myScope;
        }
  
        /// <summary>
        /// Returns the new instance of ObjectScope for the application.
        /// </summary>
        /// <returns>Instance of IObjectScope.</returns>
        /// <remarks></remarks>
        static public IObjectScope GetNewObjectScope()
        {
            Database db = Database();
  
            IObjectScope newScope = db.GetObjectScope();
            return newScope;
        }
    }
}
0
Alexander
Telerik team
answered on 15 Sep 2010, 04:57 PM
Hi Filipe Peixinho,

I spent some time trying to get this working but unfortunately with no success. As I mentioned, the metadata from all models should be merged before opening the database. I tried loading dynamically a module that works with half of the database tables and adding it to a bigger in memory metadata model that gathers the metadata from all models. However, this did not work.
The alternative is to reference one of the class models from the other model so that OpenAccess can collect all its metadata from the two assemblies. But I guess this would break the independence that you are trying to achieve between the modules.

I would suggest you to choose one of the following options:
1. Use only one class model (with one object scope provider), containing all persistent classes used in the application, and reference it from each module. This should work without any additional interventions.
2. Switch to the new visual DSL designer and create separate domain models for each module, as you have already tried to do with the "classic" wizards approach. The difference when using the designer is that merging the metadata from all models at runtime is really possible. Please let me know if you choose this option, so that I can assist you further.

Sincerely yours,
Alexander
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Filipe Peixinho
Top achievements
Rank 1
answered on 16 Sep 2010, 03:12 PM
hi!

The second choice seems the best to me.

Since i've never used that new functionality and i may up ending by wrecking up my work, can you support-me in this operations by leading some tips and links related to this issue.
0
Alexander
Telerik team
answered on 17 Sep 2010, 03:57 PM
Hi Filipe Peixinho,

You can have a look at this help chapter in order to learn how to create and use domain models. I will focus on the not so trivial part of the solution.

Let's start with some details on what is going on. OpenAccess uses a static Database object to instantiate object scopes / contexts. This object is bound to a single database and is cached so that all models using particular connection string share the same database instance. The database object also holds the OpenAccess metadata for the model that uses the database. Having this in mind, if you try to access an already initialized database from a model with metadata missing from the metadata of the database object, an exception would be thrown. To avoid this you can merge the metadata from all models that are going to use the database in a large metadata container and initialize the database for the first time this way.

Below is a class which should help you collect the metadata from the models and initialize the database.
public static class OpenAccessMetaDataHelper
{
    private static ObservableCollection<MetadataContainer> globalContainer = new ObservableCollection<MetadataContainer>();
    public static ObservableCollection<MetadataContainer> GlobalContainer
    {
        get { return globalContainer; }
        set { globalContainer = value; }
    }
    public static void RegisterMetaContainer(MetadataContainer container)
    {
        GlobalContainer.Add(container);
    }
 
    public static MetadataContainer GetMergedMetadata()
    {
        MetadataContainer metadata = new MetadataContainer();
 
        foreach (MetadataContainer container in GlobalContainer)
        {
            MergeMetaItems(metadata.PersistentTypes, container.PersistentTypes);
            MergeMetaItems(metadata.Tables, container.Tables);
            MergeMetaItems(metadata.Views, container.Views);
            MergeMetaItems(metadata.StoredProcedures, container.StoredProcedures);
            MergeMetaItems(metadata.Constraints, container.Constraints);
            MergeMetaItems(metadata.Indexes, container.Indexes);
        }
 
        return metadata;
    }
 
    static void MergeMetaItems(IList destinationCollection, IEnumerable source)
    {
        foreach (MetaItem item in source)
        {
            if (!ContainsMetaItem(destinationCollection, item))
            {
                destinationCollection.Add(item);
            }
        }
    }
 
    static bool ContainsMetaItem(IList collection, MetaItem metaItem)
    {
        foreach (MetaItem item in collection)
        {
            if (item.Name.Equals(metaItem.Name))
            {
                return true;
            }
        }
        return false;
    }
 
    /// <summary>
    /// initialize the database with the metadata merged from the modules
    /// </summary>
    public static void InitDatabase()
    {
        BackendConfiguration config = new BackendConfiguration();
        Telerik.OpenAccess.Database.Get("ConnectionString", config, GetMergedMetadata());
    }
}

You can obtain the model's metadata the following way:
XmlMetadataSource source = XmlMetadataSource.FromAssemblyResource(typeof(Customer).Assembly, "EntityDiagrams1.rlinq");
MetadataContainer container = source2.GetModel();
Where Customer is one of the persistent classes and EntityDiagrams1 is the name of your .rlinq file.

When a module is loaded, it should register its metadata by using the RegisterMetaContainer method. When all modules are loaded, the InitDatabase method should be called to create a Database instance. Then you can start instantiating contexts and using them in the application.
I am not aware of your exact setup and how you will know when a module is loaded / finished loading, maybe you will have to figure that out by yourself.

I think this should be enough for a start, just get back to us if you face a difficulty or need more details.

Sincerely yours,
Alexander
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Diego
Top achievements
Rank 1
answered on 06 Dec 2012, 08:02 PM
I see this was posted in 2010... i'm wondering if there's any major changes to that helper class to handle merging variable number of mapping configurations (via metadataSource files). I would like to use this approach with the FluentMetadataSource allowing me to create a generic data access strategy that can work with any database/data context for any project.

This link shows this done for EF and I'm trying to adapt this same code for OA:
http://code.google.com/p/ef4prs/source/browse/#svn%2Ftrunk%2Fnet45%2FInfrastructure.Data
http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/

With EF, i can dynamically load entity mapping configurations and add them to the dbcontext on instantiation. I thought about doing the same thing with OA MappingConfiguration<> classes and adding them to a FluentMetadataSource that I dynamically build, but having found your helper class maybe it's best to dynamically load the containers/FluentMetadataSource objects instead? 
0
Alexander
Telerik team
answered on 11 Dec 2012, 02:05 PM
Hello Diego,

Yes, now there is an easier approach to update the model metadata, which is described here.
You can use the AggregateMetadataSource class to merge two other metadata sources from any kind.
Once you have the merged metadata, there are two options:
1. If you have not established a connection to the database yet, just instantiate a context with the merged metadata.
2. If you have already used an OpenAccess context to read/write data, you should use the ReplaceMetadata method described in the article above. This updates the metadata which is already cached from the previous model with the new one and allows you to dynamically load new OpenAccess modules in your running application.
Hope that helps.

Kind regards,
Alexander
the Telerik team
Telerik OpenAccess ORM Meets ASP.NET Web API. Read more.
Tags
General Discussions
Asked by
Filipe Peixinho
Top achievements
Rank 1
Answers by
Alexander
Telerik team
Filipe Peixinho
Top achievements
Rank 1
Diego
Top achievements
Rank 1
Share this question
or