19

I wish to get a list of columns names, types and whether the column is a PK of a table object in Entity Framework.

How do I do this in C# (4.0) (ideally generically)?

The winning answer will be one that does it efficiently and most importantly generically.

Femaref
  • 60,705
  • 7
  • 138
  • 176
Dan B
  • 936
  • 2
  • 13
  • 26
  • For what use, and in which context? I mean, with EF you have Entities, so why not for example simply reflect your model-entities and finding out PKs by naming conventions that might be there? – sharp johnny May 19 '11 at 09:51
  • Why has my post been voted down? If you think it's a bad question - please comment and I'll explain more rather than just deface my post. – Dan B May 19 '11 at 11:12
  • @johnny Yes - I am trying to build a where clause using expressions. I have a dynamically build where clause with dynamically built lambda expressions adding a .contains for each column - but I need the table properties to both build a default collection of property names and to verify a custom passing of field names. Everything else works fine so far - just missing the EF table columns – Dan B May 19 '11 at 11:13
  • 1
    @Femaref - please explain reason for editing of post? – Dan B May 19 '11 at 11:16
  • The part edited out didn't add anything to the question at hand and in general, are frowned upon on SO anyway. – Femaref May 19 '11 at 13:56
  • Thank you Femaref - I understand now - I was being silly - sorry. – Dan B May 20 '11 at 08:05
  • So if I understand correctly, why wouldn't do simple Reflection of your model do the job? – sharp johnny May 20 '11 at 12:24
  • possible duplicate of [How to get all names of properties in an Entity?](http://stackoverflow.com/questions/5851274/how-to-get-all-names-of-properties-in-an-entity) – gbjbaanb Jun 05 '13 at 14:26

8 Answers8

10

Got it - I used a linq based reflection query:

IEnumerable<FieldList> properties = from p in typeof(T).GetProperties()
                                    where (from a in p.GetCustomAttributes(false)
                                    where a is EdmScalarPropertyAttribute   
                                    select true).FirstOrDefault()

Sorted! Thanks for the suggestions all.

FYI - I am creating a dynamic where clause using LINQ, dynamic lambda expressions to build e.g. search which will automatically search through all columns by default. But I also needed the column names to verify because I will allow this to be overridden and these calls will be done via javascript ajax post whose input cannot be trusted - so needed to verify the column names.

I used the above to place the results into a custom object with properties called FieldName, FieldType, PrimaryKey. Ta daaa.

Customise it further with

IEnumerable<FieldList> properties = from p in typeof(T).GetProperties()
                                    where (from a in p.GetCustomAttributes(false)
                                    where a is EdmScalarPropertyAttribute
                                    select true).FirstOrDefault()
                                    select new FieldList
                                    {
                                       FieldName = p.Name,
                                       FieldType = p.PropertyType,
                                       FieldPK = p.GetCustomAttributes(false).Where(a => a is EdmScalarPropertyAttribute && ((EdmScalarPropertyAttribute)a).EntityKeyProperty).Count() > 0
                                     };    
Dan B
  • 936
  • 2
  • 13
  • 26
9

if you want only column names then ,i got the best answer :
var properties = (from t in typeof(YourTableName).GetProperties() select t.Name).ToList(); var name= properties[0];

5

If you do not want to use reflection, see answer here. Replace entity name below with your entity name

var cols = from meta in ctx.MetadataWorkspace.GetItems(DataSpace.CSpace)
                       .Where(m=> m.BuiltInTypeKind==BuiltInTypeKind.EntityType)
                    from p in (meta as EntityType).Properties
                       .Where(p => p.DeclaringType.Name == "EntityName")
                   select new
                      {
                       PropertyName = p.Name,
                       TypeUsageName = p.TypeUsage.EdmType.Name, //type name
                       Documentation = p.Documentation != null ?               
                                       p.Documentation.LongDescription : null //if primary key
        };
Pete_ch
  • 1,301
  • 2
  • 28
  • 39
  • What's ctx and how do I get a handle to it? Is that for Model or Datbase first because I am using Code First – Alan Macdonald Oct 16 '13 at 13:50
  • in 3.5 its the objectcontext instance, in later EF versions its the instance of dbcontext you are using. example using var ctx = new MyEntities.... – Pete_ch Oct 16 '13 at 14:07
  • I'm on .Net 4.5 EF Code FIrst and my DBContext does not have the MetadataWorkspace property – Alan Macdonald Oct 16 '13 at 14:27
  • 1
    can you convert dbcontext to object context: ((IObjectContextAdapter)dbContext).ObjectContext; or see http://stackoverflow.com/questions/8059900/convert-dbcontext-to-objectcontext-for-use-with-gridview – Pete_ch Oct 17 '13 at 06:09
  • Thanks that worked var adapter = (IObjectContextAdapter)this; var childNavProperties = adapter.ObjectContext.MetadataWorkspace.GetItems(DataSpace.CSpace) .Where(item => item.BuiltInTypeKind == BuiltInTypeKind.EntityType) .SelectMany(item => ((EntityType)item).NavigationProperties) – Alan Macdonald Oct 17 '13 at 10:28
3

If anyone is still looking, Here's how I did it. This is an extension method for the DBContext that takes a type and returns physical column names and their properties.

This utilizes object context to get physical columns list, then uses the "PreferredName" metadata property to map each column it its property.

Since it uses object context, it initiates a database connection, so the first run will be slow depending on the complexity of the context.

public static IDictionary<String, PropertyInfo> GetTableColumns(this DbContext ctx, Type entityType)
{
    ObjectContext octx = (ctx as IObjectContextAdapter).ObjectContext;
    EntityType storageEntityType = octx.MetadataWorkspace.GetItems(DataSpace.SSpace)
        .Where(x => x.BuiltInTypeKind == BuiltInTypeKind.EntityType).OfType<EntityType>()
        .Single(x => x.Name == entityType.Name);

    var columnNames = storageEntityType.Properties.ToDictionary(x => x.Name,
        y => y.MetadataProperties.FirstOrDefault(x => x.Name == "PreferredName")?.Value as string ?? y.Name);

    return storageEntityType.Properties.Select((elm, index) =>
            new {elm.Name, Property = entityType.GetProperty(columnNames[elm.Name])})
        .ToDictionary(x => x.Name, x => x.Property);
}

To use it, just create a helper static class, and add above function; then it's as simple as calling

var tabCols = context.GetTableColumns(typeof(EntityType));
Mahmoud Hanafy
  • 1,103
  • 12
  • 12
  • 1
    This is the best answer as it returns the physical column names, which are not necessarily the same as the EF property names. – John M Oct 26 '18 at 13:24
1
typeof(TableName).GetProperties().Select(x => x.Name).ToList();
Dragon Warrior
  • 307
  • 3
  • 16
  • Although it works great for column names but is still returning also foreign keys and relations names which are not always great. – Losbaltica Feb 15 '21 at 08:40
0

I don't have a a code sample for you, but just so that you're pointed in the right direction, you might want to look into using the Sql Management Objects (SMO); you can use this to get an object hierarchy for an Sql Server instance, which you can then enumerate and pick out the information you need.

Have a look at this set of tutorials to get you started with the programming - http://www.codeproject.com/KB/database/SMO_Tutorial_1.aspx http://www.codeproject.com/KB/database/SMO_Tutorial_2.aspx

Steve Hobbs
  • 3,296
  • 1
  • 22
  • 21
  • No No - i want to examine the ef table objects in code not the sql tables. I.e. i need the property names of the ef class generated when you add a table to the ef diagram – Dan B May 19 '11 at 11:07
  • @Dan B - The "admin" removed the text `Ready, steady....GO! Thanks, Dan.`. How has the post lost part of it's meaning with this change? – Yakimych May 19 '11 at 11:51
  • @Yakimych - my mistake then - I presumed there was more. I think i had a sentance in, and then I removed it but I thought it was in. How did you find that out? I would be keen on knowing the sort of things we're not supposed to write as that doesn't seem to be in any way detrimental to the very short post. I have now removed my inaccurate comment. Can you tell who voted my post down? – Dan B May 19 '11 at 12:10
  • @Dan B - If you click on where it says he edited it, you can see an edit history. It shows you what he changed. – Tridus May 19 '11 at 12:17
  • @Dan B - Furthermore, if you don't agree with the edit of your post for some reason, you can revert it. And no, you cannot see who upvotes or downvotes your posts. I agree with you, however, that the user who makes a downvote should leave a comment explaining why. – Yakimych May 19 '11 at 12:26
  • Great thanks for the excellent info Yakimych. I will keep in mind and thank you for educating me. – Dan B May 19 '11 at 12:29
0

If you're using DB First or Model First, open up the .edmx file EF generated in a text editor. It's just an XML file, and it contains everything you need. Here's an example from a model of mine. Note that I'm using Oracle's EF driver, so yours won't look identical (but it should be pretty close).

        <EntityType Name="STRATEGIC_PLAN">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Decimal" Name="Id" Nullable="false" Precision="8" Scale="0" annotation:StoreGeneratedPattern="None" />
          <Property Type="Decimal" Name="CreatedById" Nullable="false" Precision="8" Scale="0" />
          <Property Type="DateTime" Name="CreatedDate" Nullable="false" />
          <Property Type="Decimal" Name="DepartmentId" Nullable="false" Precision="4" Scale="0" />
          <Property Type="String" Name="Name_E" Nullable="false" MaxLength="2000" FixedLength="false" Unicode="false" />
          <Property Type="String" Name="Name_F" MaxLength="2000" FixedLength="false" Unicode="false" />
          <Property Type="Decimal" Name="UpdatedById" Precision="8" Scale="0" />
          <Property Type="DateTime" Name="UpdatedDate" />
          <Property Type="DateTime" Name="Timestamp" Nullable="false" Precision="6" annotation:StoreGeneratedPattern="Computed" />
          <NavigationProperty Name="AnnualPlans" Relationship="StrategicPlanningModel.R_51213" FromRole="STRATEGIC_PLAN" ToRole="STRAT_ANNUAL_PLAN" />
          <NavigationProperty Name="Department" Relationship="StrategicPlanningModel.R_51212" FromRole="STRATEGIC_PLAN" ToRole="DEPARTMENT" />
          <NavigationProperty Name="CreatedBy" Relationship="StrategicPlanningModel.R_51210" FromRole="STRATEGIC_PLAN" ToRole="STAFF" />
          <NavigationProperty Name="UpdatedBy" Relationship="StrategicPlanningModel.R_51211" FromRole="STRATEGIC_PLAN" ToRole="STAFF" />
          <Property Type="String" Name="Desc_E" MaxLength="2000" FixedLength="false" Unicode="false" />
          <Property Type="String" Name="Desc_F" MaxLength="2000" FixedLength="false" Unicode="false" />
          <NavigationProperty Name="Goals" Relationship="StrategicPlanningModel.R_51219" FromRole="STRATEGIC_PLAN" ToRole="STRATEGIC_PLAN_GOAL" />
        </EntityType>

You can use an XML parser to parse the file and get what you need. The .edmx file contains data on both the entities and the SQL tables, so you'll need to make sure you're getting the right part of it to get what you want.

Tridus
  • 5,021
  • 1
  • 19
  • 19
  • That's cool - I will definitely come back to this but I'm still on 4.0 and I am not looking at code first (yet) just normal EF 4.0. – Dan B May 19 '11 at 11:50
  • Then it'll work. :) Code First is the case where it won't work because there is no edmx file in code first. – Tridus May 19 '11 at 11:55
  • COOL! Sorry - sleepy after lunch LOL I will take a look and let you know how I get one - CHEERS! – Dan B May 19 '11 at 12:08
0

To get only columns names without table relations eg. foreign key.

var columnNames = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanRead && !p.GetGetMethod()!.IsVirtual)
            .Select(property => property.Name)
            .ToList();

In DB context relations are usually marked as virtual objects (unless in my Postgres test).

Losbaltica
  • 609
  • 1
  • 10
  • 24
  • OF course you can't rely on the virtual modifier. Also, see "and whether the column is a PK". It's impossible to do this without accessing EF's own metadata. – Gert Arnold Feb 15 '21 at 09:19