如何在IdeaBlade 2012中获取表/字段名称?

时间:2015-12-31 02:04:50

标签: devforce

我们在项目中使用IdeaBlade 2012最新版本,在我们的帮助函数中,它显示“表名”。“字段名称”,用户可以添加注释并将它们保存在一个表中。 在EF中,它可以获取dbContext.MetadataWorkspace以获取edmx中的元数据,但是我们如何在EntityMananger中获取它?

1 个答案:

答案 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");
    }
}