csizo
csizo

Reputation: 83

EntityFramework 6.0.0-alpha3 - EdmxWriter.WriteEdmx() fails after update to alpha3 from alpha2

The following extension works for EF6 alpha2 but stopped working with alpha3 with null reference exception. The failing statement is the EdmxWriter.WriteEdmx(..)

The views pre-generation is performed on a code-first context.

How to achieve pre-generate views using EF6 alpha3?

 public static PreGeneratedViews PreGenerateViews<T>(this T dbContext) where T : DbContext
    {
        Trace.TraceInformation("PreGenerating views");

        //define ef collections
        EdmItemCollection edmItemCollection = null;
        StoreItemCollection storeItemCollection = null;
        StorageMappingItemCollection mappingItemCollection = null;

        //get ef collections
        GetItemCollections(
            GetEdmx(dbContext),
            out edmItemCollection,
            out storeItemCollection,
            out mappingItemCollection);

        IList<EdmSchemaError> errors = null;
        //get the generated views
        Dictionary<string, string> extentViews = GetExtentViews(mappingItemCollection, out errors);


        //return the pregenerated views as string (xml document)
        return new PreGeneratedViews
                   {
                       EdmEntityContainerName = edmItemCollection.GetItems<EntityContainer>().Single().Name,
                       StoreEntityContainerName = storeItemCollection.GetItems<EntityContainer>().Single().Name,
                       HashOverMappingClosure =
                           ReflectionHelper.GetMappingClosureHash(edmItemCollection.EdmVersion,
                                                                  mappingItemCollection),
                       HashOverAllExtentViews =
                           ReflectionHelper.GenerateHashForAllExtentViewsContent(edmItemCollection.EdmVersion,
                                                                                 extentViews),
                       ViewCount = extentViews.Count,
                       Views = CreateViews(extentViews),
                       ViewsEmbeddedResourceName =
                           string.Format("DbContextViews{0}.xml", Guid.NewGuid().ToString("N")),
                   };
    }

    private static XDocument GetEdmx(DbContext dbContext)
    {
        var ms = new MemoryStream();

        using (XmlWriter writer = XmlWriter.Create(ms))
        {
            EdmxWriter.WriteEdmx(dbContext, writer);
        }

        ms.Position = 0;

        return XDocument.Load(ms);
    }

    private static void SplitEdmx(XDocument edmx, out XmlReader csdlReader, out XmlReader ssdlReader,
                                  out XmlReader mslReader)
    {
        // xml namespace agnostic to make it work with any version of Entity Framework
        XNamespace edmxNs = edmx.Root.Name.Namespace;

        XElement storageModels = edmx.Descendants(edmxNs + "StorageModels").Single();
        XElement conceptualModels = edmx.Descendants(edmxNs + "ConceptualModels").Single();
        XElement mappings = edmx.Descendants(edmxNs + "Mappings").Single();

        ssdlReader = storageModels.Elements().Single(e => e.Name.LocalName == "Schema").CreateReader();
        csdlReader = conceptualModels.Elements().Single(e => e.Name.LocalName == "Schema").CreateReader();
        mslReader = mappings.Elements().Single(e => e.Name.LocalName == "Mapping").CreateReader();
    }

    private static void GetItemCollections(XDocument edmx, out EdmItemCollection edmItemCollection,
                                           out StoreItemCollection storeItemCollection,
                                           out StorageMappingItemCollection mappingItemCollection)
    {
        // extract csdl, ssdl and msl artifacts from the Edmx
        XmlReader csdlReader, ssdlReader, mslReader;
        SplitEdmx(edmx, out csdlReader, out ssdlReader, out mslReader);

        // Initialize item collections
        edmItemCollection = new EdmItemCollection(new[] {csdlReader});
        storeItemCollection = new StoreItemCollection(new[] {ssdlReader});
        mappingItemCollection = new StorageMappingItemCollection(edmItemCollection, storeItemCollection,
                                                                 new[] {mslReader});
    }

    private static Dictionary<string, string> GetExtentViews(StorageMappingItemCollection mappingItemCollection,
                                                             out IList<EdmSchemaError> errors)
    {
        Dictionary<EntitySetBase, string> views = ReflectionHelper.GenerateViews(mappingItemCollection, out errors);
        if (errors != null && errors.Any())
        {
            return null;
        }

        var extentViews = new Dictionary<string, string>(views.Count);

        foreach (var kvp in views)
        {
            extentViews.Add(
                GetExtentFullName(kvp.Key),
                kvp.Value.Replace("\r\n", "\n")); // replace accounts for Xml new line normalization
        }

        return extentViews;
    }

    private static string GetExtentFullName(EntitySetBase entitySet)
    {
        return string.Format("{0}.{1}", entitySet.EntityContainer.Name, entitySet.Name);
    }


    private static string CreateViews(Dictionary<string, string> extentViews)
    {
        var sb = new StringBuilder();
        //var embeddedViewsFileName = Path.ChangeExtension(Host.TemplateFile, "xml");
        using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings
                                                           {
                                                               Indent = true,
                                                               Encoding = Encoding.UTF8
                                                           }))
        {
            writer.WriteStartElement("views");
            foreach (var kvp in extentViews)
            {
                writer.WriteStartElement("view");
                writer.WriteAttributeString("extent", kvp.Key);
                writer.WriteCData(kvp.Value);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        return sb.ToString();
    }

    #region Nested type: ReflectionHelper

    private static class ReflectionHelper
    {
        private static readonly Assembly efAssembly = typeof (StorageMappingItemCollection).Assembly;

        private static readonly MethodInfo generateViewsMethodInfo =
            typeof (StorageMappingItemCollection).GetMethod("GenerateEntitySetViews",
                                                            BindingFlags.NonPublic | BindingFlags.Instance);

        private static readonly MethodInfo getMappingClosureHashMethodInfo =
            efAssembly.GetType("System.Data.Entity.Core.Mapping.MetadataMappingHasherVisitor", true)
                .GetMethod("GetMappingClosureHash", BindingFlags.Static | BindingFlags.NonPublic);

        private static readonly MethodInfo generateHashForAllExtentViewsContentMethodInfo =
            efAssembly.GetType("System.Data.Entity.Core.Common.Utils.MetadataHelper", true)
                .GetMethod("GenerateHashForAllExtentViewsContent", BindingFlags.Static | BindingFlags.NonPublic);

        public static Dictionary<EntitySetBase, string> GenerateViews(
            StorageMappingItemCollection mappingItemCollection, out IList<EdmSchemaError> errors)
        {
            errors = null;
            return
                (Dictionary<EntitySetBase, string>)
                generateViewsMethodInfo.Invoke(mappingItemCollection, new object[] {errors});
        }

        public static string GetMappingClosureHash(double schemaVersion,
                                                   StorageMappingItemCollection mappingItemCollection)
        {
            return (string) getMappingClosureHashMethodInfo.Invoke(
                null,
                new object[]
                    {
                        schemaVersion, 
                        // CodeFirst currently creates always one entity container
                        mappingItemCollection.GetItems<GlobalItem>().Single(
                            i => i.GetType().Name == "StorageEntityContainerMapping")
                    });
        }

        public static string GenerateHashForAllExtentViewsContent(double schemaVersion,
                                                                  Dictionary<string, string> extentViews)
        {
            return (string) generateHashForAllExtentViewsContentMethodInfo.Invoke(
                null,
                new object[] {schemaVersion, extentViews});
        }
    }

The stacktrace is the following:

NullReferenceException

Thanks!

Upvotes: 0

Views: 797

Answers (2)

Pawel
Pawel

Reputation: 31610

I just submitted a fix for work item 867 (35852e8392ad). It should fix the NRE. The fix should be included in the today's nightly build. Can you give it a try and let me know if this fixed the problem?

Upvotes: 1

Andrew Peters
Andrew Peters

Reputation: 11323

This is a known bug in Alpha 3.

Upvotes: 0

Related Questions