Odata or Ria Missing "metadata"

May 17, 2013 at 6:12 PM
Edited May 17, 2013 at 6:13 PM
I'm trying to create either a Ria service or a Odata Service using the "Reverse Engineer" toolkit to scaffold the free tables to POCO's.

From the model side all looks good with the POCO's and Map classes (keys and fields lengths...), but when trying to create the Controller (MVC\OData) or accessing RIA Via a new lightswitch project they both complain about missing metadata and wont show/create the entities.

Any insight? Anybody successfully using with RIA or ODATA?

Thanks,
Mike
May 17, 2013 at 11:12 PM
Edited May 17, 2013 at 11:13 PM
Little progress:

Seems that even though Reverse engineer builds the classes MVC4\WepAPI will not use the Fluent Models\Mapping classes for the schema so you have to define the [KEY] in the POCO's with System.ComponentModel.DataAnnotations;

Problem is now when I query the service I get

Database Container is required.

Not Sure Why it's looking for a DBC, it's a free table?
Also not sure why the stack trace is in DbCreateDatabase?

Will keep Battling!

Regards,
Mike

Stack Trace Below:

at VfpEntityFrameworkProvider.VfpProviderServices.ConvertToVfpConnection(DbConnection connection)
at VfpEntityFrameworkProvider.VfpProviderServices.DbCreateDatabase(DbConnection connection, Nullable1 commandTimeout, StoreItemCollection storeItemCollection)
at System.Data.Objects.ObjectContext.CreateDatabase()
at System.Data.Entity.Internal.DatabaseOperations.Create(ObjectContext objectContext)
at System.Data.Entity.Internal.DatabaseCreator.CreateDatabase(InternalContext internalContext, Func
3 createMigrator, ObjectContext objectContext)
at System.Data.Entity.Internal.InternalContext.CreateDatabase(ObjectContext objectContext)
at System.Data.Entity.Database.Create(Boolean skipExistsCheck)
at System.Data.Entity.CreateDatabaseIfNotExists1.InitializeDatabase(TContext context)
at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass8.<PerformDatabaseInitialization>b__6()
at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
at System.Data.Entity.Internal.LazyInternalContext.<InitializeDatabase>b__4(InternalContext c)
at System.Data.Entity.Internal.RetryAction
1.PerformAction(TInput input)
at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action1 action)
at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
at System.Data.Entity.Internal.InternalContext.Initialize()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet
1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet1.get_InternalContext()
at System.Data.Entity.Internal.Linq.InternalSet
1.Find(Object[] keyValues)
at System.Data.Entity.DbSet1.Find(Object[] keyValues)
at CifWebApi.Controllers.BcLogController.GetBclog(String id) in c:\Work\CifWebApi\CifWebApi\Controllers\BcLogController.cs:line 29
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func
1 func, CancellationToken cancellationToken)
Coordinator
May 17, 2013 at 11:57 PM
Edited May 17, 2013 at 11:58 PM
The default code first setup assumes that you are actually doing code first and expects to have to create your data structure for you. You can override this default by calling Database.SetInitializer<TContext>(null). I typically set this in a static constructor of the DbContext.

Here is an example:
    public class VfpDbContext : DbContext {
        public IDbSet<Customer> Customers { get; set; }

        static VfpDbContext() {
            Database.SetInitializer<VfpDbContext>(null);
        }
    }
May 18, 2013 at 6:25 PM
Thanks Tom, Ill give it a try

As always, your a wealth of information!

Regards,
Mike
May 21, 2013 at 2:08 AM
Hey Tom,

Just Some more information for people who might be trying the same thing, and an additional question:

Seems my learning curve continues as I learn the differences between webapi and odata. I assumed that just adding [Queryable] to the method that it would magicly generate odata and lightswitch would see it. Nope, MVC4 build classes from ApiController, for odata you need to change this to ODataController or EntitySetController<Type,KeyType>. Also MVC creates it's webapi classes from iEnumerable where odata likes IQueryable.

So my Controller looks like the Following
    public class DbfsController : EntitySetController<Dbf, string> // Was ApiController
    {
        private DBFContext db = new DBFContext();

        [Queryable]
        public override IQueryable<Dbf> Get()
        {
            return db.Dbfs.AsQueryable();
        }

        protected override Dbf GetEntityByKey(string key)
        {
            return db.Dbfs.FirstOrDefault(p => p.ID == key);
        }

        protected override Dbf PatchEntity(string key, Delta<Dbf> patch)
        {
            Dbf DbfToPatch = db.Dbfs.FirstOrDefault(p => p.ID == key);
            patch.Patch(DbfToPatch);
            return DbfToPatch;
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
Second Piece of the puzzle was the WebApiConfig Class that I commented out the api routes and added Odata ones
            //config.Routes.MapHttpRoute(
            //    name: "DefaultApi",
            //    routeTemplate: "api/{controller}/{id}",
            //    defaults: new { id = RouteParameter.Optional }
            //);

            ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
            modelBuilder.EntitySet<Dbf>("Dbfs");

            Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
            config.Routes.MapODataRoute("ODataRoute", "odata", model);
third piece of the puzzle was the ContextClass (and Toms help from above to stop the database from trying to create)
    public class DBFContext: DbContext
    {
        static DBFContext()
        {
            Database.SetInitializer<DBFContext>(null);
        }

        public DBFContext() : base("name=DBFContext")
        {
        }
 
        public DbSet<Dbf> Dbfs{ get; set; }
  }
The question I still have and the additional step is the Toolkits "Reverse Engineer" function scaffolds the POCO classes, but also builds the Fluent Mapping classes where the rest of the EF world seems to like Data annotations. Therefore, I have to go into the model classes and redefine the [KEY].

The fluent Classes are there with fields lengths "HasMaxLength(7)" and Keys "this.HasKey(t => t.ID)" How does you hook them?
   public partial class Dbf
    {
        [KEY]
        public string ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string Phone { get; set; }
   }
Any incite on how to get EF\ODATA\MVC can use these Fluent Mapping the toolkit emits?

Thanks again,
Mike
Coordinator
May 21, 2013 at 1:35 PM
I haven't worked with EntitySetController or ODataController so I'm not sure if this will help... but I notice that you are not overriding the OnModelCreating method. This method is where you add your mapping.

Example:
public class DBFContext : DbContext {
    public DbSet<Dbf> Customers { get; set; }

    static DBFContext() {
        Database.SetInitializer<DBFContext>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        // DbfMap should have been generated by the reverse engineer tool.
        modelBuilder.Configurations.Add(new DbfMap());
    }
}
May 23, 2013 at 12:34 PM
Edited May 23, 2013 at 12:36 PM
So far from everything Ive read, ODataModelBuilder does not allow Fluent mappings in EF4 or EF5. The .GetEdmModel() is From the POCO's and there is no way to annotate Fluent like you can Data annotations.

There was some talk about it under the Nightly Webapi stack that I am looking into but believe it requires EF6
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Dbf>("Dbfs");
//            modelBuilder.Configurations.Add(new DbfMap());  <---- NO GOOD - Needs Class from DBContext we only have a model builder :(
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
Curious, how are you're using your driver - maybe I should stick to what your doing. (lol)

Thanks,
Mike