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 });
}
}
}
```
Running the above repro gives the following result:
```
8e34da82f0a463d58eaf78e78ecd008c86f5505f37542f2ec4a8c386cfd81b1f
2b7265642c9879fc8bbd85ba3960919c16cbb72d90f66f2a64fa178deb22bb22
Press any key to continue . . .
```
Expected result - hash values should be the same since this is the same model.
Here is the first difference in the hash visitor dump (note I got it from a different project):
****
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 });
}
}
}
```
Running the above repro gives the following result:
```
8e34da82f0a463d58eaf78e78ecd008c86f5505f37542f2ec4a8c386cfd81b1f
2b7265642c9879fc8bbd85ba3960919c16cbb72d90f66f2a64fa178deb22bb22
Press any key to continue . . .
```
Expected result - hash values should be the same since this is the same model.
Here is the first difference in the hash visitor dump (note I got it from a different project):
****
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