Domanda Ottieni il nome della tabella del database da MetaData di Entity Framework


Sto cercando di capire un modo per ottenere il nome della tabella SQL sottostante per un dato tipo di entità. Ho sperimentato con le query di MetadataWorkspace e mentre posso ottenere molte informazioni dall'oggetto o dallo spazio di archiviazione, non riesco a capire come mappare tra i due.

Quindi dì che ho un tipo nel modello di oggetti chiamato Ricerca - come trovo il tablename (wws_lookups) nel database?

Posso interrogare tutti gli oggetti EntityType per CSpace e SSpace e posso vedere entrambi elencati correttamente ma non riesco a capire come ottenere SSpace da CSpace.

C'è un modo per fare questo?


44
2017-12-13 03:16


origine


risposte:


Uso l'approccio di Nigel (estrarre il nome della tabella da .ToTraceString()) ma con alcune modifiche, perché il suo codice non funzionerà se la tabella non è nello schema predefinito di SQL Server (dbo.{table-name}).

Ho creato i metodi di estensione per DbContext e ObjectContext oggetti:

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;
    }
}

Maggiori dettagli qui:
Entity Framework: ottieni il nome della tabella mappata da un'entità


36
2018-03-18 18:01



MODIFICARE Questa risposta ora obsoleta a causa della nuova funzionalità di EF 6.1: mappatura tra tipi di tabella. Vai prima!

Ho avuto un problema con le altre risposte perché ho un tipo derivato. Ho ottenuto questo metodo (all'interno della mia classe di contesto) per funzionare - ho solo un livello di ereditarietà nel mio modello al momento

private readonly static Dictionary<Type, EntitySetBase> _mappingCache 
       = new Dictionary<Type, EntitySetBase>();

private ObjectContext _ObjectContext
{
    get { return (this as IObjectContextAdapter).ObjectContext; }
}

private EntitySetBase GetEntitySet(Type type)
{
    if (_mappingCache.ContainsKey(type))
        return _mappingCache[type];

    type = GetObjectType(type);
    string baseTypeName = type.BaseType.Name;
    string typeName = type.Name;

    ObjectContext octx = _ObjectContext;
    var es = octx.MetadataWorkspace
                    .GetItemCollection(DataSpace.SSpace)
                    .GetItems<EntityContainer>()
                    .SelectMany(c => c.BaseEntitySets
                                    .Where(e => e.Name == typeName 
                                    || e.Name == baseTypeName))
                    .FirstOrDefault();

    if (es == null)
        throw new ArgumentException("Entity type not found in GetEntitySet", typeName);

    // Put es in cache.
    _mappingCache.Add(type, es);

    return es;
}

internal String GetTableName(Type type)
{
    EntitySetBase es = GetEntitySet(type);

    //if you are using EF6
    return String.Format("[{0}].[{1}]", es.Schema, es.Table);

    //if you have a version prior to EF6
    //return string.Format( "[{0}].[{1}]", 
    //        es.MetadataProperties["Schema"].Value, 
    //        es.MetadataProperties["Table"].Value );
}

internal Type GetObjectType(Type type)
{
    return System.Data.Entity.Core.Objects.ObjectContext.GetObjectType(type);
}

NB Ci sono piani per migliorare l'API dei metadati e se questo non sta ottenendo ciò che vogliamo, allora possiamo guardare Prima mappatura del codice EF tra tipi e tabelle


24
2017-09-23 17:06



No, sfortunatamente è impossibile utilizzare le API dei metadati per ottenere il tablename per una determinata entità.

Questo perché i metadati di Mapping non sono pubblici, quindi non c'è modo di passare da C-Space a S-Space usando le API di EF.

Se tu veramente devi farlo puoi sempre costruire la mappa da solo analizzando MSL. Questo non è per i deboli di cuore, ma dovrebbe essere possibile, a meno che non stiate usando QueryViews (che sono incredibilmente rari), a quel punto è a tutti gli effetti impossibile (dovresti analizzare ESQL ... argh!)

Alex James

Microsoft.


6
2017-12-13 06:04



C'è un modo per cancellare i dati usando EF senza dover prima caricarlo. L'ho descritto in un po 'più di detenzione in: http://nigelfindlater.blogspot.com/2010/04/how-to-delete-objects-in-ef4-without.html

Il trucco consiste nel castare IQueriable in ObjectQuery e utilizzare il metodo ToTraceString. Quindi modificare la stringa sql risultante. Funziona ma devi stare attento perché stai bypassando i meccanismi che EF ha in atto per mantenere dipendenze e dipendenze. Ma per motivi di prestazioni, penso che sia giusto farlo ....

divertiti...

Nigel ...

    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)); 
    } 

6
2018-04-29 06:58



Se si utilizza il modello T4 per le classi POCO, è possibile ottenerlo modificando il modello T4. Vedi lo snippet:

<#  
////////////////////////////////////////////////////////////////////////////////
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();

////////////////////////////////////////////////////////////////////////////////

4
2018-06-03 19:38



Una soluzione possibile (non eccezionale, ma nemmeno le alternative ...):

var sql = Context.EntitySetName.ToTraceString();

... quindi analizza l'SQL, che dovrebbe essere abbastanza semplice.


3
2017-12-14 15:08



Ecco cosa sono riuscito a utilizzare LINQ in XML. Il codice ottiene anche i mapping per i nomi delle colonne.

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();

3
2018-05-05 18:21