56

特定のエンティティ タイプの基になる SQL テーブル名を取得する方法を見つけようとしています。MetadataWorkspace クエリを試してみましたが、オブジェクトまたはストレージ スペースから多くの情報を取得できますが、この 2 つをマッピングする方法がわかりません。

オブジェクト モデルに Lookup という型があるとします。データベースでテーブル名 (wws_lookups) を見つけるにはどうすればよいでしょうか。

CSpace と SSpace のすべての EntityType オブジェクトを照会でき、両方が正しくリストされているのを確認できますが、CSpace から SSpace を取得する方法がわかりません。

これを行う方法はありますか?

4

25 に答える 25

42

私は Nigel のアプローチ ( からテーブル名を抽出する.ToTraceString()) を使用しますが、テーブルが既定の SQL Server スキーマ ( ) にない場合、彼のコードは機能しないため、いくつかの変更を加えていますdbo.{table-name}

DbContextおよびObjectContextオブジェクトの拡張メソッドを作成しました。

public static class ContextExtensions
{
    public static string GetTableName<T>(this DbContext context) where T : class
    {
        ObjectContext objectContext = ((IObjectContextAdapter) context).ObjectContext;

        return objectContext.GetTableName<T>();
    }

    public static string GetTableName<T>(this ObjectContext context) where T : class
    {
        string sql = context.CreateObjectSet<T>().ToTraceString();
        Regex regex = new Regex(@"FROM\s+(?<table>.+)\s+AS");
        Match match = regex.Match(sql);

        string table = match.Groups["table"].Value;
        return table;
    }
}

詳細はこちら:
エンティティ フレームワーク: エンティティからマップされたテーブル名を取得する

于 2012-03-18T18:01:52.007 に答える
6

残念ながら、メタデータ API を使用して特定のエンティティのテーブル名を取得することはできません。

これは、Mapping メタデータが公開されていないためです。そのため、EF の API を使用して C スペースから S スペースに移動する方法はありません。

本当にこれを行う必要がある場合は、MSL を解析していつでも自分でマップを作成できます。これは気弱な人向けではありませんが、 QueryViews (信じられないほどまれです) を使用していない限り、可能であるはずです。 )

アレックス・ジェームズ

マイクロソフト。

于 2009-12-13T06:04:15.113 に答える
5

EFを使用してデータを削除する方法があります。最初にデータをロードする必要はありません。もう少し詳しく説明しました:http://nigelfindlater.blogspot.com/2010/04/how-to-delete-objects-in-ef4 -without.html

秘訣は、IQueriableをObjectQueryにキャストし、ToTraceStringメソッドを使用することです。次に、結果のSQL文字列を編集します。それは機能しますが、依存関係と制約を維持するためにEFが備えているメカニズムをバイパスしているため、注意する必要があります。しかし、パフォーマンス上の理由から、これを行っても問題ないと思います。

楽しんで...

ナイジェル...

    private string GetClause<TEntity>(IQueryable<TEntity> clause) where TEntity : class 
    { 
        string snippet = "FROM [dbo].["; 

        string sql = ((ObjectQuery<TEntity>)clause).ToTraceString(); 
        string sqlFirstPart = sql.Substring(sql.IndexOf(snippet)); 

        sqlFirstPart = sqlFirstPart.Replace("AS [Extent1]", ""); 
        sqlFirstPart = sqlFirstPart.Replace("[Extent1].", ""); 

        return sqlFirstPart; 
    } 

   public void DeleteAll<TEntity>(IQueryable<TEntity> clause) where TEntity : class 
    { 
        string sqlClause = GetClause<TEntity>(clause); 
        this.context.ExecuteStoreCommand(string.Format(CultureInfo.InvariantCulture, "DELETE {0}", sqlClause)); 
    } 
于 2010-04-29T06:58:33.990 に答える
5

EF6 で codefirst を実行している場合は、次のようなものを dbcontext クラスに追加するだけです。

    public string GetTableName(Type entityType)
    {
        var sql = Set(entityType).ToString();
        var regex = new Regex(@"FROM \[dbo\]\.\[(?<table>.*)\] AS");
        var match = regex.Match(sql);

        return match.Groups["table"].Value;
    }
于 2015-01-17T19:44:54.000 に答える
4

POCOクラスにT4テンプレートを使用している場合は、T4テンプレートを変更することで取得できます。スニペットを参照してください:

<#  
////////////////////////////////////////////////////////////////////////////////
region.Begin("Custom Properties");

string xPath = "//*[@TypeName='" + entity.FullName + "']";
XmlDocument doc = new XmlDocument();
doc.Load(inputFile);

XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2008/10/edmx");

XmlNode item;
XmlElement root = doc.DocumentElement;
item = root.SelectSingleNode(xPath);

#>
    //<#= xPath #>
    //<#= entity.FullName #>
    //<#= (item == null).ToString() #>

<# if (item != null) #>
// Table Name from database
public string TableName { get { return "<#= item.ChildNodes[0].Attributes["StoreEntitySet"].Value #>"; } }
<#

region.End();

////////////////////////////////////////////////////////////////////////////////
于 2011-06-03T19:38:02.273 に答える
3

より良い方法は、メタデータのStoreItemCollectionを使用することです。この男はすでにそれを使用する例を提供しています: テーブルと関係を取得する

于 2012-11-12T11:30:37.237 に答える
3

EF 6.1、コードファースト:

public static string GetTableName<T>(this DbContext context) where T : class
{
    ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
    return objectContext.GetTableName(typeof(T));
}

public static string GetTableName(this DbContext context, Type t)
{
    ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
    return objectContext.GetTableName(t);
}

private static readonly Dictionary<Type,string> TableNames = new Dictionary<Type, string>();

public static string GetTableName(this ObjectContext context, Type t)
    {
        string result;

        if (!TableNames.TryGetValue(t, out result))
        {
            lock (TableNames)
            {
                if (!TableNames.TryGetValue(t, out result))
                {

                    string entityName = t.Name;

                    ReadOnlyCollection<EntityContainerMapping> storageMetadata = context.MetadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace);

                    foreach (EntityContainerMapping ecm in storageMetadata)
                    {
                        EntitySet entitySet;
                        if (ecm.StoreEntityContainer.TryGetEntitySetByName(entityName, true, out entitySet))
                        {
                            if (String.IsNullOrEmpty(entitySet.Schema))
                            {
                                result = entitySet.Table;
                                break;
                            }


                            //we must recognize if we are under SQL Server Compact version, which does not support multiple schemas
                            //SQL server compact does not support schemas, entity framework sets entitySet.Schema set to "dbo", anyway
                            //the System.Data.Entity.Infrastructure.TableExistenceChecker.GetTableName() returns only table name
                            //schema is (not) added by the overrides of the method AnyModelTableExistsInDatabase
                            //the SqlCeTableExistenceChecker has the knowledge that there is no metadata schema needed
                            //the SqlTableExistenceChecker has the knowledge that there is metadata with schema, which should be added to the table names

                            var entityConnection = (System.Data.Entity.Core.EntityClient.EntityConnection) context.Connection;

                            DbConnection storeConnection = entityConnection.StoreConnection;

                            if (storeConnection != null && "SqlCeConnection".Equals(storeConnection.GetType().Name, StringComparison.OrdinalIgnoreCase))
                            {
                                result = entitySet.Table;
                                break;
                            }

                            result = entitySet.Schema  + "." + entitySet.Table;
                            break;
                        }
                    }

                    TableNames.Add(t,result);
                }
            }
        }

        return result;
    }
于 2014-11-30T19:00:25.417 に答える
3

可能な回避策(素晴らしいものではありませんが、代替手段でもありません...):

var sql = Context.EntitySetName.ToTraceString();

...次に、SQL を解析します。これは非常に単純なはずです。

于 2009-12-14T15:08:07.400 に答える
3

LINQ to XML を使用して思いついたものを次に示します。このコードは、列名のマッピングも取得します。

var d = XDocument.Load("MyModel.edmx");
XNamespace n = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
var l = (from etm in d.Descendants()
            where etm.Name == n + "EntityTypeMapping"
            let s = etm.Attribute("TypeName").Value
            select new
            {
                Name = s.Remove(0, s.IndexOf(".") + 1).Replace(")", ""),
                Table = etm.Element(n + "MappingFragment").Attribute("StoreEntitySet").Value,
                Properties = (from sp in etm.Descendants(n + "ScalarProperty")
                            select new
                            {
                                Name = sp.Attribute("Name").Value,
                                Column = sp.Attribute("ColumnName").Value
                            }).ToArray()
            }).ToArray();
于 2012-05-05T18:21:46.787 に答える
2

テーブル名を見つける別の方法があります。少し奇妙ですが、機能します。VB:

For Each Table In northwind.MetadataWorkspace.GetItemCollection(New System.Data.Metadata.Edm.DataSpace)
        'adds table name to a list of strings all table names in EF have the project namespace in front of it.'
        If Table.ToString.Contains("namespace of project") then
            'using substring to remove project namespace from the table name.'
            TableNames.Add(Table.ToString.Substring("length of namespace name"))      
        End If
    Next
于 2010-12-20T20:29:51.803 に答える
2

バージョン 3.0 および 3.1 では、かなり簡単です:

var myClassTableName = _context.Model.FindEntityType(typeof(MyClass)).GetTableName();
于 2020-09-27T23:29:15.800 に答える
1

EF5 と少しリフレクションを使用すると、次のようなことがうまくいくはずです。

using System;
using System.Collections;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Reflection;

namespace EFHelpers {
    public class EFMetadataMappingHelper {
        public static string GetTableName(MetadataWorkspace metadata, DbEntityEntry entry) {
            var entityType = entry.Entity.GetType();

            var objectType = getObjectType(metadata, entityType);
            var conceptualSet = getConceptualSet(metadata, objectType);
            var storeSet = getStoreSet(metadata, conceptualSet);
            var tableName = findTableName(storeSet);

            return tableName;
        }

        private static EntitySet getStoreSet(MetadataWorkspace metadata, EntitySetBase entitySet) {
            var csSpace = metadata.GetItems(DataSpace.CSSpace).Single();
            var flags = BindingFlags.NonPublic | BindingFlags.Instance;
            var entitySetMaps = (ICollection)csSpace.GetType().GetProperty("EntitySetMaps", flags).GetValue(csSpace, null);

            object mapping = null;

            foreach (var map in entitySetMaps) {
                var set = map.GetType().GetProperty("Set", flags).GetValue(map, null);
                if (entitySet == set) {
                    mapping = map;
                    break;
                }
            }

            var m_typeMappings = ((ICollection)mapping.GetType().BaseType.GetField("m_typeMappings", flags).GetValue(mapping)).OfType<object>().Single();
            var m_fragments = ((ICollection)m_typeMappings.GetType().BaseType.GetField("m_fragments", flags).GetValue(m_typeMappings)).OfType<object>().Single();
            var storeSet = (EntitySet) m_fragments.GetType().GetProperty("TableSet", flags).GetValue(m_fragments, null);

            return storeSet;
        }

        private static string findTableName(EntitySet storeSet) {
            string tableName = null;

            MetadataProperty tableProperty;

            storeSet.MetadataProperties.TryGetValue("Table", true, out tableProperty);
            if (tableProperty == null || tableProperty.Value == null)
                storeSet.MetadataProperties.TryGetValue("http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Table", true, out tableProperty);

            if (tableProperty != null)
                tableName = tableProperty.Value as string;

            if (tableName == null)
                tableName = storeSet.Name;

            return tableName;
        }

        private static EntityType getObjectType(MetadataWorkspace metadata, Type entityType) {                
            var objectItemCollection = (ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace);

            var edmEntityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .First(e => objectItemCollection.GetClrType(e) == entityType);

            return edmEntityType;
        }

        private static EntitySetBase getConceptualSet(MetadataWorkspace metadata, EntityType entityType) {
            var entitySetBase = metadata
                .GetItems<EntityContainer>(DataSpace.CSpace)
                .SelectMany(a => a.BaseEntitySets)
                .Where(s => s.ElementType.Name == entityType.Name)
                .FirstOrDefault();

            return entitySetBase;
        }
    }
}

次のように呼び出します。

public string GetTableName(DbContext db, DbEntityEntry entry) {
    var metadata = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
    return EFMetadataMappingHelper.GetTableName(metadata, entry);
}
于 2015-05-14T11:21:06.097 に答える
1

これは、コンテキストがあり、実際のテーブル名を見つける必要がある選択されたエンティティがメモリ内にあると仮定したバージョンです。

    
    パブリック静的クラス ObjectContextExtentions
    {
        public static string TableNameFor (この ObjectContext コンテキスト、ObjectStateEntry エントリ)
        {
            var ジェネリック =
                context.GetType().GetProperties().ToList().First(p => p.Name == entry.EntityKey.EntitySetName);
            var objectset = generic.GetValue(context, null);

            var メソッド = objectset.GetType().GetMethod("ToTraceString");
            var sql = (文字列)method.Invoke(objectset, null);

            var match = Regex.Match(sql, @"FROM\s+\[dbo\]\.\[(?<TableName>[^\]]+)\]", RegexOptions.Multiline);
            if (一致.成功)
            {
                return match.Groups["TableName"].Value;
            }

            throw new ArgumentException("テーブル名が見つかりません。");
        }
    }
于 2011-02-06T07:57:55.183 に答える
0

EF6の場合、他の回答のコードをこことその周辺で混合/圧縮します(VB、申し訳ありません):

    Public Function getDBTableName(data As myDataModel, ByVal entity As Object) As String
        Dim context = CType(data, IObjectContextAdapter).ObjectContext
        Dim sName As String = entity.GetType.BaseType.Name 'use BaseType to avoid proxy names'
        Dim map = context.MetadataWorkspace.GetItems(Of EntityContainerMapping)(DataSpace.CSSpace).FirstOrDefault

        Return (From esmap In map.EntitySetMappings 
                Select esmap.EntityTypeMappings.First(
                    Function(etm) 
                     etm.EntityType.Name = sName
                   ).Fragments.First.StoreEntitySet.Name).FirstOrDefault
        'TODO: use less .first everywhere but filter the correct ones'
    End Function

これは、db-first で機能します。
.edmx ファイルに従うと、比較的簡単に理解できます。

于 2015-09-30T16:55:32.110 に答える
0

Alex の言うとおりです。これは Metadata API の悲しい制限です。MSL を XML ドキュメントとしてロードし、C 空間モデルを処理しながら S 空間エンティティを検索するだけです。

于 2009-12-13T06:06:53.973 に答える