none
EF 4.1 DataService does not expose properties with NotMapped Attribute

    Question

  • I have an entity with a property that is calculated and it is not on the database table where the entity is mapped. This property is decorated with the attribute NotMapped.

    This entity is expose through a DataService but in the client side i cannot see this property. 

    ¿Is this a bug?

     

    Thanks in advance,



    • Moved by Jackie-SunModerator Monday, April 04, 2011 3:28 AM (From:ADO.NET Entity Framework and LINQ to Entities)
    Tuesday, March 29, 2011 4:42 PM

Answers

  • From speaking to some of the folks responsible for the Entity Framework, it sounds like have properties in the Conceptual Model (the part of the model exposed by the data service) that are not mapped is not supported.

    Based on this, I can think of two other options you may want to investigate:

    1) Compute the string in the database itself, though I'm not sure if this can be automatically inferred in CodeFirst
    Edit: From talking to the EF folks, this cannot be done. They've proposed just adding a dummy setter to the computed property and allowing there to be a column in the DB even though you won't use it. The value will still be computed by your class in the middle tier.

    2) Compute the string on the Data-Service client type. The generated types are all partial classes, and you could add the property there instead.

    I realize this is not ideal. Like I mentioned, extending the service's model independent of the EF or other provider is a very common request, and one we hope to support soon.


    Matt Meehan (WCF Data Services / OData)
    Thursday, April 21, 2011 5:40 PM
    Moderator

All replies

  • Hi,

    Can you provide the output from the /$metadata endpoint of your service? Client codegen is based on what is found there.

    I'm not familiar with the NotMapped attribute; is the property present in the CSDL or Conceptual-space portion of your entity model? What about the entity classes themselves?


    Matt Meehan WCF Data Services / OData
    Tuesday, April 19, 2011 10:32 PM
    Moderator
  • Hi,

    I'm using Entity Framework 4.1 and MSDN documention for NotMapped attribute is "Denotes that a property or class should be excluded from database mapping."

     

    I've created a console application to expose this scenario:

     

     

    using System;

    using System.ComponentModel.DataAnnotations;

    using System.Data.Entity;

    using System.Data.Entity.Infrastructure;

    using System.Data.Objects;

    using System.Data.Services;

    using System.Data.Services.Common;

    namespace DataServiceConsole

    {

        class Program

        {

            static private Uri address = new Uri("http://localhost:2272/NotMappedService.svc");

            static private DataServiceHost host;

     

            static void Main(string[] args)

            {

     

                host = new DataServiceHost(typeof(CTPDataService), new Uri[] { address });

                host.Open();

     

                Console.WriteLine("Web Service Host");

     

     

     

                Console.ReadKey();

     

                host.Close();

            }

        }

        [Table("ACCOUNT_ADDRESS")]

        public class AccountAddress

        {

            [Key]

            [Column("ADDRESS_ID")]

            public long AddressId { get; set; }

            [Column("CITY")]

            public string City { get; set; }

            [Column("COUNTRY")]

            public string Country { get; set; }

     

            [NotMapped]

            public string CityCountry { get { return String.Format("{0} City is in {1}", this.City, this.Country); } }

     

            // public ICollection<Account> Accounts { get; set; }

        }

     

        [Table("ACCOUNT")]

        public class Account

        {

            [Key]

            [Column("ACCOUNT_ID")]

            public long AccountId { get; set; }

     

            [Column("CODE")]

            public string Code { get; set; }

     

            [Column("DESCRIPTION")]

            public string Description { get; set; }

     

            [Column("ADDRESS_ID")]

            public long AddressId { get; set; }

     

            [ForeignKey("AddressId")]

            public AccountAddress Address { get; set; }

        }

     

        public class MyContext : DbContext

        {

            public MyContext()

                : base("MyContext")

            {

            }

     

            public DbSet<AccountAddress> AddressSet { get; set; }

            public DbSet<Account> AccountSet { get; set; }

     

            protected override void OnModelCreating(DbModelBuilder modelBuilder)

            {

                modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.ColumnTypeCasingConvention>();

            }

        }

        class CTPDataService : DataService<ObjectContext>

        {

            MyContext context = new MyContext();

     

            protected override ObjectContext CreateDataSource()

            {

                context.Configuration.ValidateOnSaveEnabled = true;

                context.Configuration.LazyLoadingEnabled = true;

                context.Configuration.ProxyCreationEnabled = false;

                return ((IObjectContextAdapter)context).ObjectContext;

            }

     

     

            public static void InitializeService(DataServiceConfiguration config)

            {

                config.SetEntitySetAccessRule("AccountSet", EntitySetRights.All);

                config.SetEntitySetAccessRule("AddressSet", EntitySetRights.All);

                config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);

                config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;

                config.DataServiceBehavior.AcceptProjectionRequests = true;

                config.UseVerboseErrors = true;

            }

     

            [ChangeInterceptor("AccountSet")]

            [ChangeInterceptor("AddressSet")]

            public void OnUpdateOperation(object source, UpdateOperations operation)

            {

                if (operation == UpdateOperations.Add)

                {

                    Account account = source as Account;

                    if (account != null)

                    {

                        //if (account.AddressId == 0 && account.Address == null)

                        //    throw new DataServiceException(402, "Account requires an Address");

                    }

                }

            }

        }

    }

    The $metadata shows the following xml base:
      <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
    - <edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
    - <edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="1.0" m:MaxDataServiceVersion="2.0">
    - <Schema Namespace="DataServiceConsole" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
    - <EntityType Name="AccountAddress">
    - <Key>
      <PropertyRef Name="AddressId" />
      </Key>
      <Property Name="AddressId" Type="Edm.Int64" Nullable="false" p8:StoreGeneratedPattern="Identity" xmlns:p8="http://schemas.microsoft.com/ado/2009/02/edm/annotation" />
      <Property Name="City" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" />
      <Property Name="Country" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" />
      </EntityType>
    - <EntityType Name="Account">
    - <Key>
      <PropertyRef Name="AccountId" />
      </Key>
      <Property Name="AccountId" Type="Edm.Int64" Nullable="false" p8:StoreGeneratedPattern="Identity" xmlns:p8="http://schemas.microsoft.com/ado/2009/02/edm/annotation" />
      <Property Name="Code" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" />
      <Property Name="Description" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" />
      <Property Name="AddressId" Type="Edm.Int64" Nullable="false" />
      <NavigationProperty Name="Address" Relationship="DataServiceConsole.Account_Address" FromRole="Account_Address_Source" ToRole="Account_Address_Target" />
      </EntityType>
    - <Association Name="Account_Address">
      <End Role="Account_Address_Source" Type="DataServiceConsole.Account" Multiplicity="*" />
    - <End Role="Account_Address_Target" Type="DataServiceConsole.AccountAddress" Multiplicity="1">
      <OnDelete Action="Cascade" />
      </End>
    - <ReferentialConstraint>
    - <Principal Role="Account_Address_Target">
      <PropertyRef Name="AddressId" />
      </Principal>
    - <Dependent Role="Account_Address_Source">
      <PropertyRef Name="AddressId" />
      </Dependent>
      </ReferentialConstraint>
      </Association>
      </Schema>
    - <Schema Namespace="System.Data.Objects" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
    - <EntityContainer Name="MyContext" m:IsDefaultEntityContainer="true">
      <EntitySet Name="AddressSet" EntityType="DataServiceConsole.AccountAddress" />
      <EntitySet Name="AccountSet" EntityType="DataServiceConsole.Account" />
    - <AssociationSet Name="Account_Address" Association="DataServiceConsole.Account_Address">
      <End Role="Account_Address_Source" EntitySet="AccountSet" />
      <End Role="Account_Address_Target" EntitySet="AddressSet" />
      </AssociationSet>
      </EntityContainer>
      </Schema>
      </edmx:DataServices>
      </edmx:Edmx>
    I hope it will be helpfull..
    Thanks in advance

     

    Thursday, April 21, 2011 1:17 PM
  • From looking at your $metadata output, its clear that the client-side types will not have the 'CityCountry' property, because its not there.

    When the data-services server runs on top of Entity Framework, it gets the $metadata information directly from the MetadataWorkspace of the ObjectContext/DbContext. I will follow up with the Code-First team to see if its possible to include the property in the model despite it not being present in the database.

    There is no way currently to do this at the Data-Services layer, it needs to be provided by the EF. Being able to control this is a very common feature ask, and one we are looking to address in a future release.


    Matt Meehan (WCF Data Services / OData)
    Thursday, April 21, 2011 5:08 PM
    Moderator
  • From speaking to some of the folks responsible for the Entity Framework, it sounds like have properties in the Conceptual Model (the part of the model exposed by the data service) that are not mapped is not supported.

    Based on this, I can think of two other options you may want to investigate:

    1) Compute the string in the database itself, though I'm not sure if this can be automatically inferred in CodeFirst
    Edit: From talking to the EF folks, this cannot be done. They've proposed just adding a dummy setter to the computed property and allowing there to be a column in the DB even though you won't use it. The value will still be computed by your class in the middle tier.

    2) Compute the string on the Data-Service client type. The generated types are all partial classes, and you could add the property there instead.

    I realize this is not ideal. Like I mentioned, extending the service's model independent of the EF or other provider is a very common request, and one we hope to support soon.


    Matt Meehan (WCF Data Services / OData)
    Thursday, April 21, 2011 5:40 PM
    Moderator
  • Hello,

    Any cleaner solution with the lastest version of EF and oData ?

    Regards,

    Patrice


    MCPD SharePoint Developer 2010

    Tuesday, January 22, 2013 10:49 AM
  • So (obviously long time since OP) you can do this with latest version of EF and oData WebAPI simply by NOT adding the NotMapped attribute, and using a calculated get and an empty set.

    EF will NOT attempt to save the property (n.b. I'm not using code first - for that I guess you'd want a Fluent mapping to ignore the property) but the $metadata will include the property and it will get materialized on the client.

    Would be nice to have explicit control over this with attributes so that e.g. the client doesn't bother to change track / include in Delta patches etc, but it does work just fine.  

    Property is on client with correct value and EF doesn't blow up on server on inserts / updates so all good. 

    Tuesday, April 15, 2014 11:11 PM