Fixes for TableSchemaModelAdapter

Jul 4, 2011 at 10:55 AM
Edited Jul 4, 2011 at 10:56 AM

Dear Community,

The TableSchemaModelAdapter wasn't working for me. I tracked down the issue and finally found the following:

In ConnectionAdapter.cs, line 123, we see the following code:

foreach(var storeEntitySet in xml.Descendants(StoreNamespace + "EntitySet"))

This is supposed to select all EntitySet XML nodes in the Entity Framework EDMX file.

Unfortunately this fails to work, maybe because this project was last modified in 2009 and there might have been schema changes since then.

Anyway, by rewriting this line as follows:

foreach (var storeEntitySet in xml.Descendants().Where(i => i.Name.LocalName == "EntitySet"))

we achieve the same effect and the TableSchemaModelAdapter will work again. (You'll also need to include System.Linq at the beginning of the ConnectionAdapter.cs file for the above line to work.)

This is a great project, thanks for the effort!

Jul 4, 2011 at 11:07 AM

Another bit of information that might come in handy (BennyM has hinted on this on another discussion thread, but he didn't disclose specifics):

If you want to do the schema transformation multiple times, i.e. if you are building a multitenant application, then you'll be surprised to find that creating a new Entity Framework object context won't correctly change your schema. This is because the schema transformation is cached in a static variable and Entity Framework Runtime Model Adapter will fetch it from the cache for any new instance of the object context.

The solution is very simple, once again: just remove the static keyword from lines 22 and 28 in ConnectionAdapter.cs.

Old versions of the lines:

        private static IDictionary<string, MetadataWorkspace> metadataMapping = new Dictionary<string, MetadataWorkspace>();
        protected static IDictionary<string, MetadataWorkspace> MetadataMapping { get { return metadataMapping; } }

New versions of the lines:

        private IDictionary<string, MetadataWorkspace> metadataMapping = new Dictionary<string, MetadataWorkspace>();
        protected IDictionary<string, MetadataWorkspace> MetadataMapping { get { return metadataMapping; } }
Jul 11, 2011 at 6:50 PM

Thank you for posting this.  The multiple schema transformation was driving me nuts.

Jun 7, 2012 at 9:31 AM

Thanks ShunterAlhena.  It works now!!!  Thank you so much.

I wonder why the samples that comes with BrandonHaynes' site still work without the above modification, but it doesn't work in my own project.

Jun 12, 2012 at 10:32 AM

Thanks to ShunterAlhena for saving me the difficulty of identifying this issue with the static dictionary, lovely simple solution.

Jun 13, 2012 at 5:07 AM

Wow, guys, glad I could help. :)

Nov 12, 2012 at 4:12 PM
Edited Nov 12, 2012 at 4:12 PM

I had to make another modification to TableSchemaModelAdapter to solve some specific situacion.

The first EntitySet was containing "store:Schema" attribute instead of "Schema". Also, it contained some query calling the wrong schema.

I changed TableSchemaModelAdapter class with this code and it worked:

using System.Xml.Linq;

namespace ModelAdapterEF
{
    /// <summary>
    /// Adapts a workspace by changing the database owner to the specified schema value
    /// </summary>
    public class TableSchemaModelAdapter : BaseDecoratorModelAdapter
    {
        /// <summary>
        /// The database schema to use during adaptation
        /// </summary>
        protected string Schema { get; set; }

        /// <summary>
        /// The database schema to use during adaptation enclosed with brackets
        /// </summary>
        protected string SchemaWithBrackets { get; set; }

        /// <summary>
        /// Schema attribute name
        /// </summary>
        private const string SchemaAttribute = "Schema";

        /// <summary>
        /// Schema special attribute name
        /// </summary>
        private const string SchemaSpecialAttribute =
            "{http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator}Schema";

        /// <summary>
        /// Defining Query element special name
        /// </summary>
        private const string DefiningQueryElement = "{http://schemas.microsoft.com/ado/2009/02/edm/ssdl}DefiningQuery";

        /// <summary>
        /// Opening bracket char
        /// </summary>
        private const string OpeningBracketChar = "[";

        /// <summary>
        /// Closing bracket char
        /// </summary>
        private const string ClosingBracketChar = "]";

        /// <summary>
        /// Instantiates a TableSchemaModelAdapter using the given database schema
        /// </summary>
        /// <param name="schema">The database schema to use during adaptation</param>
        public TableSchemaModelAdapter(string schema)
            : base()
        {
            Schema = schema;
            SchemaWithBrackets = OpeningBracketChar + schema + ClosingBracketChar;
        }

        /// <summary>
        /// Instantiates a TableSchemaModelAdapter using the given schema AND decorates the passed-in
        /// IModelAdapter
        /// </summary>
        /// <param name="schema">The schema to use during adaptation</param>
        /// <param name="decoratedModel">The IModelAdapter to decorate</param>
        public TableSchemaModelAdapter(string schema, IModelAdapter decoratedModel)
            : base(decoratedModel)
        {
            Schema = schema;
            SchemaWithBrackets = OpeningBracketChar + schema + ClosingBracketChar;
        }

        #region IModelAdapter Members

        public override void AdaptStoreEntitySet(XElement storeEntitySet)
        {
            var xAttribute = storeEntitySet.Attribute(SchemaAttribute);
            if (xAttribute != null)
            {
                xAttribute.Value = Schema;
            }
            else
            {
                xAttribute = storeEntitySet.Attribute(SchemaSpecialAttribute);

                if (xAttribute != null)
                {
                    var fromSchema = OpeningBracketChar + xAttribute.Value + ClosingBracketChar;
                    xAttribute.Value = Schema;

                    if (storeEntitySet.HasElements)
                    {
                        var xElement = storeEntitySet.Element(DefiningQueryElement);
                        if (xElement != null)
                            xElement.Value = storeEntitySet.Value.Replace(fromSchema, SchemaWithBrackets);
                    }
                }
            }

            base.AdaptStoreEntitySet(storeEntitySet);
        }

        #endregion
    }
}