For the same model view gen can create different hashes depending on how views are generated. The preferred way of generating views is to load StorageMappingItemCollection from xml artifacts obtained from EdmxWriter and call GenerateViews on this item collection. At runtime the hash used to compare whether views are up to date are created from collections loaded differently. Apparently there are slight differences between the two collection which - after changing the way we calculate the hash - result in different hashes (view definitions are the same). As a result views are unusable - running a project fails with: ```System.Data.Entity.Core.MappingException: The mapping and metadata information for EntityContainer 'MyContext' no longer matches the information used to create the pre-generated views.```
Repro:
```
namespace ViewGenTest
{
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Role> Roles { get; set; }
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
}
class Program
{
static void Main(string[] args)
{
GenerateViewsAndWriteHash(GetMappingItemCollectionFromDbContext());
GenerateViewsAndWriteHash(GetMappingItemCollectionFromEdmx());
}
private static void GenerateViewsAndWriteHash(StorageMappingItemCollection mappingItemCollection)
{
IList<EdmSchemaError> errors = new List<EdmSchemaError>();
var viewGroups = mappingItemCollection.Generate(errors);
Console.WriteLine(viewGroups.Single().MappingHash);
}
private static StorageMappingItemCollection GetMappingItemCollectionFromDbContext()
{
using (var ctx = new MyContext())
{
return
(StorageMappingItemCollection)((IObjectContextAdapter)ctx).ObjectContext
.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
}
}
private static StorageMappingItemCollection GetMappingItemCollectionFromEdmx()
{
return GetMappingItemCollection(GetEdmx(typeof(MyContext)));
}
private static XDocument GetEdmx(Type contextType)
{
var ms = new MemoryStream();
using (var writer = XmlWriter.Create(ms))
{
EdmxWriter.WriteEdmx((DbContext)Activator.CreateInstance(contextType), 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
var edmxNs = edmx.Root.Name.Namespace;
var storageModels = edmx.Descendants(edmxNs + "StorageModels").Single();
var conceptualModels = edmx.Descendants(edmxNs + "ConceptualModels").Single();
var 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 StorageMappingItemCollection GetMappingItemCollection(XDocument edmx)
{
// extract csdl, ssdl and msl artifacts from the Edmx
XmlReader csdlReader, ssdlReader, mslReader;
SplitEdmx(edmx, out csdlReader, out ssdlReader, out mslReader);
// Initialize item collections
var edmItemCollection = new EdmItemCollection(new XmlReader[] { csdlReader });
var storeItemCollection = new StoreItemCollection(new XmlReader[] { ssdlReader });
return new StorageMappingItemCollection(edmItemCollection, storeItemCollection, new XmlReader[] { mslReader });
}
}
}
```
Here is the first difference in the hash visitor dump:
****
System.Data.Entity.Core.Metadata.Edm.TypeUsage Instance#66
System.Data.Entity.Core.Metadata.Edm.RefType Instance#59
Instance Reference: 59
****
System.Data.Entity.Core.Metadata.Edm.TypeUsage Instance#66
System.Data.Entity.Core.Metadata.Edm.RefType Instance#67
Transient.reference[CodeFirstDatabaseSchema.RoleUser]
System.Data.Entity.Core.Metadata.Edm.EntityType Instance#47
Instance Reference: 47
Repro:
```
namespace ViewGenTest
{
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Role> Roles { get; set; }
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
}
class Program
{
static void Main(string[] args)
{
GenerateViewsAndWriteHash(GetMappingItemCollectionFromDbContext());
GenerateViewsAndWriteHash(GetMappingItemCollectionFromEdmx());
}
private static void GenerateViewsAndWriteHash(StorageMappingItemCollection mappingItemCollection)
{
IList<EdmSchemaError> errors = new List<EdmSchemaError>();
var viewGroups = mappingItemCollection.Generate(errors);
Console.WriteLine(viewGroups.Single().MappingHash);
}
private static StorageMappingItemCollection GetMappingItemCollectionFromDbContext()
{
using (var ctx = new MyContext())
{
return
(StorageMappingItemCollection)((IObjectContextAdapter)ctx).ObjectContext
.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
}
}
private static StorageMappingItemCollection GetMappingItemCollectionFromEdmx()
{
return GetMappingItemCollection(GetEdmx(typeof(MyContext)));
}
private static XDocument GetEdmx(Type contextType)
{
var ms = new MemoryStream();
using (var writer = XmlWriter.Create(ms))
{
EdmxWriter.WriteEdmx((DbContext)Activator.CreateInstance(contextType), 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
var edmxNs = edmx.Root.Name.Namespace;
var storageModels = edmx.Descendants(edmxNs + "StorageModels").Single();
var conceptualModels = edmx.Descendants(edmxNs + "ConceptualModels").Single();
var 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 StorageMappingItemCollection GetMappingItemCollection(XDocument edmx)
{
// extract csdl, ssdl and msl artifacts from the Edmx
XmlReader csdlReader, ssdlReader, mslReader;
SplitEdmx(edmx, out csdlReader, out ssdlReader, out mslReader);
// Initialize item collections
var edmItemCollection = new EdmItemCollection(new XmlReader[] { csdlReader });
var storeItemCollection = new StoreItemCollection(new XmlReader[] { ssdlReader });
return new StorageMappingItemCollection(edmItemCollection, storeItemCollection, new XmlReader[] { mslReader });
}
}
}
```
Here is the first difference in the hash visitor dump:
****
System.Data.Entity.Core.Metadata.Edm.TypeUsage Instance#66
System.Data.Entity.Core.Metadata.Edm.RefType Instance#59
Instance Reference: 59
****
System.Data.Entity.Core.Metadata.Edm.TypeUsage Instance#66
System.Data.Entity.Core.Metadata.Edm.RefType Instance#67
Transient.reference[CodeFirstDatabaseSchema.RoleUser]
System.Data.Entity.Core.Metadata.Edm.EntityType Instance#47
Instance Reference: 47