我们在项目中使用IdeaBlade 2012最新版本,在我们的帮助函数中,它显示“表名”。“字段名称”,用户可以添加注释并将它们保存在一个表中。 在EF中,它可以获取dbContext.MetadataWorkspace以获取edmx中的元数据,但是我们如何在EntityMananger中获取它?
答案 0 :(得分:0)
正如金约翰逊在评论中所提到的,自定义代码生成可以很好地解决这个问题。我们已在我们的应用中完成了此操作,以便为包含表/列名称的每个实体和字段添加其他注释。这有助于C#开发人员,因为我们有时重命名实体/属性并尝试将它们映射回后端可能会很棘手。
然而,最近我们想要从EF模型中获取更多数据,并且已经直接解析XML EDMX。这为我们提供了我们想要的所有信息。它甚至将EDMX数据与DevForce元数据甚至反射数据相关联。
以下是感兴趣的人的完整代码。这是很多代码,但希望它很容易理解。它可能比大多数人需要的更多。
/// <summary>
/// Helper class for parsing the EDMX model, looking up DevForce metadata as well as C# reflection data and then combining it all
/// into organized data structures.
/// </summary>
public static class ModelDataExtractor
{
/// <summary>
/// Information about a property/column from an EDMX.
/// </summary>
public class EdmxColumnInfo
{
public string TableName { get; set; }
public string ColumnName { get; set; }
public string SqlType { get; set; }
public bool? Nullable { get; set; }
public int? MaxLength { get; set; }
public bool? FixedLength { get; set; }
public bool? IsUnicode { get; set; }
}
/// <summary>
/// Collection of information about a property.
/// </summary>
public class ExtraPropertyInfo
{
public PropertyInfo CSharpProperty { get; set; }
public EntityProperty DevForceProperty { get; set; }
public EdmxColumnInfo EdmxData { get; set; }
public string TypeHierarchy { get; set; }
}
/// <summary>
/// Extracts the data from the EDMX's embeded in the Business assembly.
/// </summary>
public static List<ExtraPropertyInfo> ExtractData()
{
var assembly = typeof(MyFavoriteEntity).Assembly;
var all = from name in assembly.GetManifestResourceNames()
where name.EndsWith(".csdl") || name.EndsWith(".msl") || name.EndsWith(".ssdl")
let xml = XElement.Load(assembly.GetManifestResourceStream(name))
select new EdmxFragment(name.Replace(".csdl", "").Replace(".msl", "").Replace(".ssdl", ""), xml);
return ExtractData(all.ToList());
}
/// <summary>
/// Extracts the data from the EDMX's in the given directory
/// </summary>
public static List<ExtraPropertyInfo> ExtractData(string dataModelPath)
{
return ExtractData(
Directory.GetFiles(dataModelPath, "*.edmx")
.Select(fileName => new EdmxFragment(fileName, XElement.Load(fileName)))
.ToList());
}
/// <summary>
/// Provides the ability for multiple XElements to be treated as multiple fragments of the same logical EDMX.
/// </summary>
private class EdmxFragment
{
public EdmxFragment(string edmxName, XElement documentRoot)
{
EdmxName = edmxName;
DocumentRoot = documentRoot;
}
public string EdmxName { get; private set; }
public XElement DocumentRoot { get; private set; }
}
private static List<ExtraPropertyInfo> ExtractData(IList<EdmxFragment> edmxFragments)
{
//If we aren't given any fragments...well, nothing to do really
if (edmxFragments.Count == 0)
{
return new List<ExtraPropertyInfo>();
}
//Derive the correct namespaces to use
var ns = EdmxNamespaceHelper.DeriveNamespaces(edmxFragments[0].DocumentRoot);
//First extract data out of the EDMXs 'sections'
//Parse the SSDL data for each XML document
var storageTypes = from fragment in edmxFragments
from entityType in fragment.DocumentRoot.Descendants(XName.Get("EntityType", ns.Ssdl))
from prop in entityType.Elements(XName.Get("Property", ns.Ssdl))
select new
{
TableName = entityType.GetString("Name"),
ColumnName = prop.GetString("Name"),
SqlType = prop.GetString("Type"),
Nullable = prop.GetBool("Nullable"),
MaxLength = prop.GetInt("MaxLength"),
fragment.EdmxName
};
//Parse the CSDL data for each XML document
var conceptualTypes = from fragment in edmxFragments
from entityType in fragment.DocumentRoot.Descendants(XName.Get("EntityType", ns.Csdl))
from prop in entityType.Elements(XName.Get("Property", ns.Csdl))
select new
{
TypeName = entityType.GetString("Name"),
PropertyName = prop.GetString("Name"),
FixedLength = prop.GetBool("FixedLength"),
IsUnicode = prop.GetBool("Unicode"),
fragment.EdmxName
};
//Parse the MSDL data for each XML document
var mappings = from fragment in edmxFragments
from setMap in fragment.DocumentRoot.Descendants(XName.Get("EntitySetMapping", ns.Msdl))
let typeMap = setMap.Elements(XName.Get("EntityTypeMapping", ns.Msdl)).Single()
let mapFragment = typeMap.Elements(XName.Get("MappingFragment", ns.Msdl)).Single()
from prop in mapFragment.Elements(XName.Get("ScalarProperty", ns.Msdl))
let fullTypeName = typeMap.GetString("TypeName")
let typeName = fullTypeName.Substring(fullTypeName.LastIndexOf(".", StringComparison.Ordinal) + 1)
select new
{
TypeName = typeName,
TableName = mapFragment.GetString("StoreEntitySet"),
PropertyName = prop.GetString("Name"),
ColumnName = prop.GetString("ColumnName"),
fragment.EdmxName
};
//Using the mapping info to join the Conceptual and Storage info
var tempCombined = from storageInfo in storageTypes
join mapping in mappings
on new { storageInfo.ColumnName, storageInfo.TableName, storageInfo.EdmxName }
equals new { mapping.ColumnName, mapping.TableName, mapping.EdmxName }
join conceptualInfo in conceptualTypes
on new { mapping.PropertyName, mapping.TypeName, mapping.EdmxName } equals
new { conceptualInfo.PropertyName, conceptualInfo.TypeName, conceptualInfo.EdmxName }
select new
{
storageInfo,
mapping,
conceptualInfo
};
//Flatten the information from the Conceptual and Storage info
var edmxInfo = from c in tempCombined
select new
{
c.mapping.TypeName,
c.mapping.PropertyName,
ColumnInfo = new EdmxColumnInfo
{
TableName = c.storageInfo.TableName,
ColumnName = c.storageInfo.ColumnName,
SqlType = c.storageInfo.SqlType,
Nullable = c.storageInfo.Nullable,
MaxLength = c.storageInfo.MaxLength,
IsUnicode = c.conceptualInfo.IsUnicode,
FixedLength = c.conceptualInfo.FixedLength,
}
};
//Build a dictionary for easy retrieval
var fullEdmxData = edmxInfo.ToDictionary(d => d.TypeName + "." + d.PropertyName,
d => d.ColumnInfo);
//Use Reflection and DevForce metadata to inspect the C# code
var results =
//Go through all the Types in assembly that 'my favorite entity' is in
from cSharpType in typeof(MyFavorateEntity).Assembly.GetTypes()
//Ignore abstract classes (the virtual entities probably)
where !cSharpType.IsAbstract
//Only process types that are Entities
where cSharpType.IsSubclassOf(typeof(Entity))
//Build the class hierarchy as a string
let hierarchy = GetAncestors(cSharpType).GetDelimitedString(" > ", t => t.Name)
//Let all the C# properties (except the XYZProperty ones)
let codeProperties = cSharpType.GetProperties()
//Get the metadata from DevForce
let modelMetadata = EntityMetadataStore.Instance.GetEntityMetadata(cSharpType)
//Combine the C# data, the DevForce data and the EDMX data
let combined = from codeProperty in codeProperties
join modelProperty in modelMetadata.EntityProperties
on codeProperty.Name equals modelProperty.Name into
matchedModelProperties
let singleMatchedModelProperty = matchedModelProperties.SingleOrDefault()
let typePropString = cSharpType.Name + "." + codeProperty.Name
let edmxData =
fullEdmxData.ContainsKey(typePropString)
? fullEdmxData[typePropString]
: null
select new ExtraPropertyInfo
{
TypeHierarchy = hierarchy,
CSharpProperty = codeProperty,
DevForceProperty = singleMatchedModelProperty,
EdmxData = edmxData
}
from c in combined
select c;
return results.ToList();
}
private static IEnumerable<Type> GetAncestors(Type t)
{
var parent = t.BaseType;
while (parent != typeof(Entity) && parent != null)
{
yield return parent;
parent = parent.BaseType;
}
}
}
/// <summary>
/// Helper class to dynamically determine the EDMX version (and therefore namespace names) of a particular EDMX file (or fragment)
/// </summary>
internal class EdmxNamespaceHelper
{
public string Edmx { get; private set; }
public string Csdl { get; private set; }
public string Ssdl { get; private set; }
public string Msdl { get; private set; }
/// <summary>
/// Version 2 namespaces
/// </summary>
private static readonly EdmxNamespaceHelper V2 = new EdmxNamespaceHelper
{
Edmx = "http://schemas.microsoft.com/ado/2008/10/edmx",
Csdl = "http://schemas.microsoft.com/ado/2008/09/edm",
Ssdl = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl",
Msdl = "http://schemas.microsoft.com/ado/2008/09/mapping/cs"
};
/// <summary>
/// Version 3 namespaces
/// </summary>
private static readonly EdmxNamespaceHelper V3 = new EdmxNamespaceHelper
{
Edmx = "http://schemas.microsoft.com/ado/2009/11/edmx",
Csdl = "http://schemas.microsoft.com/ado/2009/11/edm",
Ssdl = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl",
Msdl = "http://schemas.microsoft.com/ado/2009/11/mapping/cs"
};
/// <summary>
/// Sees is this instance's namespaces seem to be valid for the given <see cref="XElement"/> root.
/// </summary>
private bool IsValid(XElement xmlRoot)
{
try
{
//See if the top element is the Runtime element (for .edmx files)
return xmlRoot.Element(XName.Get("Runtime", Edmx)) != null ||
//or if the root has a namespace of one of the individual sections (for fragments)
xmlRoot.Name.NamespaceName.In(Edmx, Csdl, Ssdl, Msdl);
}
catch
{
return false;
}
}
/// <summary>
/// Determines the correct <see cref="EdmxNamespaceHelper"/> for the given <see cref="XElement"/> root.
/// </summary>
public static EdmxNamespaceHelper DeriveNamespaces(XElement xmlRoot)
{
//Try V2 and V3. If none of them seem to match, just error out.
if (V2.IsValid(xmlRoot))
return V2;
if (V3.IsValid(xmlRoot))
return V3;
throw new InvalidOperationException("Could not determine EDMX type of given XML Document");
}
}