Issue with inheritance with WebAPI, OData v3 and BreezeJs - entity-framework

We are using WebAPI 2.2, with OData v3 and BreezeJS and are having an issue when using inheritance, we have a setup along the following lines (simplified obviously for this issue)
We have a Vehicle abstract class and then two other classes (Bus and Car) which inherit from Vehicle, such as:
public abstract class Vehicle
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Bus : Vehicle
{
public int NumberOfSeats { get; set; }
}
public class Car : Vehicle
{
public string Colour { get; set; }
}
We then have an Activity class which can have a single Vehicle (either a Car or a Bus):
public class Activity
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime DueDate { get; set; }
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
We would like to be able to query the Activity and expand its Vehicle and get the relevent Car or Bus back, such as https://dummysitename/api/Activities?$expand=Vehicle, which is working fine. We would also like to be able to GET/PATCH/POST to endpoints for Cars and Buses (such as https://dummysitename/api/Cars and https://dummysitename/api/Buses), however we get a 404 when trying to do this.
Our metadata is created by the breeze EdmBuilder. We have tested it not using the EdmBuilder but using the ODataConventionModelBuilder and that works fine for these scenarios, but doesn't obviously work for us in the grander scheme of things as we will be utilizing breeze.js heavily.
Any ideas on why we can't use the Cars and Buses endpoints when using the EdmBuilder would be greatly appreciated.
UPDATE:
It would appear that the issue is being caused by the Vehicle being stipulated on the Activity class. With the Vehicle on the Activity class the EntityContainer section of the metadata looks like this:
<EntityContainer Name="TodoListContext" p5:UseClrTypes="true" xmlns:p5="http://schemas.microsoft.com/ado/2013/11/edm/customannotation">
<EntitySet Name="Activities" EntityType="ODataBreezejsSample.Models.Activity" />
<EntitySet Name="Vehicles" EntityType="ODataBreezejsSample.Models.Vehicle" />
<AssociationSet Name="Activity_Vehicle" Association="ODataBreezejsSample.Models.Activity_Vehicle">
<End Role="Activity_Vehicle_Source" EntitySet="Activities" />
<End Role="Activity_Vehicle_Target" EntitySet="Vehicles" />
</AssociationSet>
</EntityContainer>
however if the Vehicle is removed from the Activity class then that same section looks like this:
<EntityContainer Name="TodoListContext" p5:UseClrTypes="true" xmlns:p5="http://schemas.microsoft.com/ado/2013/11/edm/customannotation">
<EntitySet Name="Activities" EntityType="ODataBreezejsSample.Models.Activity" />
<EntitySet Name="Buses" EntityType="ODataBreezejsSample.Models.Bus" />
<EntitySet Name="Cars" EntityType="ODataBreezejsSample.Models.Car" />
</EntityContainer>
at which point the Bus and Car endpoints become available, however this is not really an option as we require the Activity to contain the base class Vehicle.

Related

"Object reference not set to an instance of an object" when creating a new Web API controller with EF Scaffolding in Visual Studio 2012

I have an MVC4/Web API project, with an Entity Framework, Code First data model. When I try to create a new API Controller with read/write methods using a data context & model, I get an alert saying "Object reference not set to an instance of an object".
I've done a bit of searching and found that some causes are incorrect project type Guids in the .csproj file, incomplete installation of the MvcScaffolding nuget package and one suggestion of installing Powershell 3.
I have made sure all my project type guids are correct, made sure the MvcScaffolding package is installed correctly, and I've even installed Powershell 3.
None of this has solved the problem for me. All I can think is there is a problem with my data context/model although it created the tables/relationships fine. Code below:
Context:
public class PropertySearchContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Property>().HasRequired(p => p.Office).WithMany(o => o.Properties).HasForeignKey(p => p.OfficeId);
}
public DbSet<Office> Offices { get; set; }
public DbSet<Property> Properties { get; set; }
}
Model:
[Serializable]
public class Property
{
public int PropertyId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
public int Bedrooms { get; set; }
public int Bathrooms { get; set; }
public string UmbracoNodeId { get; set; }
public string MainImageUrl { get; set; }
public string ListingImageUrl { get; set; }
public int TotalImageCount { get; set; }
public PropertyType PropertyType { get; set; }
public PropertyStatus PropertyStatus { get; set; }
public long Price { get; set; }
public string ListingUrl { get; set; }
//Navigation Properties
public int OfficeId { get; set; }
public virtual Office Office { get; set; }
//Meta properties
public DateTime CreatedAt { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedAt { get; set; }
public string UpdatedBy { get; set; }
}
Connection String:
<add name="PropertySearchContext" connectionString="Data Source=MERCURY\SQLEXPRESS;Initial Catalog=DATABASE_NAME;Integrated Security=False;User ID=dbFakeUser;Password=fakePassword;Connect Timeout=10" providerName="System.Data.SqlClient" />
Any help with this would be greatly appreciated as I've tried every suggestion and I still can't create a controller with scaffolding. Driving me mad!
Thanks!
Found the problem. In my model, I had a property with a custom enum type, which was in my business project. In my service project, I had my data model project referenced but not the business project. So adding a reference to the model AND business project allowed me to add scaffold controllers fine.
Seems obvious I know, but the error message it gives you is so unhelpful!
Anyway, I hope this helps anyone having the same problem, and can't fix it using the other suggestions.
I am adding an answer as my problem was the same and my solution was different. It had no relation to the referenced assemblies. First of all, I believe it only happened because the Web API project had just been created (from scratch).
I will give you some code examples based on the OP code. First, my context class that inherits from DbContext had to call the base constructor passing as argument the connection string name:
public class PropertySearchContext : DbContext
{
public PropertySearchContext () : base("name=PropertySearchContext")
Second, the Web API application would internally look inside Web.config for a connection string named PropertySearchContext. Web.config already comes with a DefaultConnection, so I just had to add a new one with the proper name and settings:
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\DATABASE_NAME.mdf;Initial Catalog=DATABASE_NAME;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="PropertySearchContext" connectionString="Data Source=MERCURY\SQLEXPRESS;Initial Catalog=DATABASE_NAME;Integrated Security=False;User ID=dbFakeUser;Password=fakePassword;Connect Timeout=10" providerName="System.Data.SqlClient" />
</connectionStrings>
note: the OQ already has that connection string.
Third and finally, I had to build the WebAPI project. Once successful, the creation of controllers worked fine.
I get this error if I am attempting to Scaffold a controller with views for a Model that does not contain a default constructor(one that does not need parameters). Add one to the model and try again. This has bitten me more than once.
The newer compilers works just fine without one, as if it does not see one it just inserts one for you. However the scaffold operation needs that default constructor be actually declared in code to operate correctly. I just wish it gave you a better explanation when you get the error.
So for the original post simply adding this to the class :
public Property(){}
should do the trick.
If you have more than one project in your solution try to rebuild all.
This answer depends on many things. On my case I didn't have a problem with the model itself. After long hours looking I discovered that another project, which was being referenced on my MVC .NET website, was the one stopping the scaffolding from running correctly.
What I am doing (kind of tedious) is to remove the reference (breaks many things meanwhile), Add the controllers/views that I need, then add the reference again and voila. No "Object Reference" error.
Hope this works for you

Error Entity Framework Database Changed

I have started with ASP.NET MVC and the Entity Framework recently.
With an existing Database, I tried to map my classes to the database tables. Without success.
Let me show you my database tables:
-> Categories
--> Id (PK)
--> Title
--> Description
-> Products
--> Id (PK)
--> Name
--> Description
--> Price
--> Category (FK -> CategoryTree)
-> CategoriyTree
--> Id (PK)
--> ParentCategory (FK -> Category)
--> ChildCategory (FK -> Category)
Now, my classes for the Entity Framework:
class Products
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal? Price { get; set; }
public virtual ICollection<CategoryTree> Category { get; set; }
}
class Categories
{
public int categoriesId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
class CategoryTree
{
public int Id { get; set; }
public virtual ICollection<Categories> ParentCategory { get; set; }
public virtual ICollection<Categories> ChildCategory { get; set; }
}
class EFDbContext : DbContext
{
public DbSet<Products> Products { get; set; }
public DbSet<Categories> Categories { get; set; }
public DbSet<CategoryTree> CategoryTree { get; set; }
}
If I start the application, the compiler alerts the following error message:
The model backing the 'EFDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
I hope someone can help me to understand this ;-)
Best regards
In the Application_Start() method of your Global.asax.cs, add the following line:
Database.SetInitializer<EFDbContext>(null);
That will stop Entity Framework from checking for changes to your model since the creation of your database.
For an existing database, I would suggest use EF power tool to reverse engineering to generate POCO classes.
The only difference I can see from the above information id Categories.Id in the db and Categories.categoriesId in the model (assuming no typo).
Also check the nullability of columns, for example Product.Price is nullable in the model, is it so in the db?
Can I also suggest, generate a new db with a different name from your model and compare to the existing db. The db name is specified in the connection string, so add a constructor to you DbContext class and pass the connection string in,
internal class EFDbContext : DbContext
{
public EFDbContext(string connectionString) : base(connectionString)
{
}

Code first is keep creating a database with the fully qualified name of my context

This is my context class
public class HospitalContext : DbContext
{
public DbSet<Patient> Patients { get; set; }
public DbSet<Doctor> Doctors { get; set; }
public DbSet<Appointment> Appointments { get; set; }
public DbSet<Schedule> Schedule { get; set; }
}
And my connection string
<add name="DbContext"
providerName="System.Data.SqlClient"
connectionString="Data Source=(LocalDb)\v11.0;
Initial Catalog=HospitalProject;
Integrated Security=True;" />
I'd like to know why when I run the application, the database name is
HospitalProject.Models.HospitalContext
instead of HospitalProject.
Thanks for helping
Try renaming connection string to HospitalContext instead of DbContext

Update Model From Database (Database First)

I'm using MVC3 VS2010 with EF4.1, I have created my DB using SQL Server and I import it to the MVC3 Web Application.
I have a challenge here, when I come to Update Model from Database I do lost all my models files modifications, for example if I'm using attributes in some models for validation or so all that is overwritten with the new model properties.
Is there anyway to Update Model from Database without losing models' information?
OR
where should I define validation on my models instead of using the models' files directly?
Update: As this is still relatively popular, I have created a blog post on this.
http://jnye.co/Posts/19/adding-validation-to-models-created-by-entity-framework-database-first-c
If you want to validate your models, and not use viewModels, use partial classes to define validation attributes. For example:
Say you have a model like
public class User {
public string Name { get; set; }
}
If you wanted to put a string length validator on it you would need to create a partial class and utilise the MetadataTypeAttribute (this lives in System.ComponentModel.DataAnnotations)
The following classes should be defined in their own separate file, NOT put in the same file as your auto generated models.
[MetadataTypeAttribute(typeof(UserMetadata))]
public partial class User {
}
You then define your validation in the UserMetadata class as follows
public class UserMetadata{
[StringLength(50)]
public string Name {get; set;}
}
EDIT
I just found this article which explains the solution in a little more detail
http://themonitoringguy.com/tips-tricks/validating-microsoft-entity-framework-objects-c-mvc/
No, the files will be regenerated every time.
All the classes are defined as partial so you can easily add DataAnnotations using the MetadataTypeAttribute.
Let's say you have a User class defined as follow:
public partial class User {
public string Name {get;set;}
}
Create a IUser interface
public interface IUser {
[Required]
[DisplayName("User name")]
string Name {get;set;}
}
And then extend the User class to specify that IUser will be used as metadata.
[MetadataType(typeof(IUser))]
public partial class User {} //Empty class body
The first rule of any designer is: It it generates any code you can't modify it because it will be completely deleted next time you update anything in the designer.
All generated classes are partial so you can create your own partial part and put your custom logic there. You obviously can't add attributes to properties defined in auto generated part. In case of data annotations it is possible either through buddy classes or by custom T4 template which will contain your own logic to decide which data annotation should be added during code generation. Both scenarios are mostly considered as a bad practice because you should have separate view model per view with validation needed exactly for that view.
Check the namespace of the MainClass is same as Partial, and have the same Attributes. That is my solution.
example:
Metadata: Create this everywhere u want
public class FormMetadata
{
public int Id { get; set; }
public string Description { get; set; }
public Nullable<bool> IsEnable { get; set; }
public Nullable<System.DateTime> CreationDate { get; set; }
public int CompanieId { get; set; }
public string RegularExpression { get; set; }
public virtual ICollection<Field> Fields { get; set; }
[JsonIgnore]
public virtual Company Company { get; set; }
}
MainClass
namespace Transactions.Model
{
public partial class Form
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Form()
{
this.Fields = new HashSet<Field>();
}
public int Id { get; set; }
public string Description { get; set; }
public Nullable<bool> IsEnable { get; set; }
public Nullable<System.DateTime> CreationDate { get; set; }
public int CompanieId { get; set; }
public string RegularExpression { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Field> Fields { get; set; }
public virtual Company Company { get; set; }
}
}
Partial To Use the MetadataType
namespace Transactions.Model
{
[MetadataTypeAttribute(typeof(FormMetadata))]
public partial class Form
{
}
}
If you have problems to Create a Class Partial in the same NameSpace? Don't worry:
Create a Folder
Create the Class Partial in this folder
Change Namespace at the same of MainClass

Unique keys in Entity Framework 4

An existing DB schema has unique, non-primary, keys, and some foreign keys that rely on them.
Is it possible to define unique keys, which are not primary keys, in Entity Framework v4? How?
The Entity Framework 6.1 now supports uniques with both Data Annotations and Fluent API.
Data Annotations (Reference)
public class MyEntityClass
{
[Index(IsUnique = true)]
[MaxLength(255)] // for code-first implementations
public string MyUniqueProperty{ get; set; }
}
Fluent API (Reference)
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<MyEntityClass>()
.Property(t => t.MyUniqueProperty)
.HasMaxLength(255) // for code-first implementations
.HasColumnAnnotation(
"Index",
new IndexAnnotation(new[]
{
new IndexAttribute("Index") { IsUnique = true }
})));
}
}
}
You have to apply an index and set the unique property to true. By default, indexes are non-unique according to documentation.
And also you have to install the Entity Framework 6.1 NuGet package in your project in order to use the new API for indexes.
Note about code-first implementations: A VARCHAR(MAX) cannot be part of a unique constraint. You must specify the maximum length either as a Data Annotation or in the Fluent API.
See also this MSDN blog post: http://blogs.msdn.com/b/efdesign/archive/2011/03/09/unique-constraints-in-the-entity-framework.aspx. In brief, this isn't supported in V4, though the EF team seems to have plans to support it in future releases.
I came across the same problem not long ago.
I was given a database with a few tables (see below).
public class ClinicDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Doctor> Doctors { get; set; }
public DbSet<Patient> Patients { get; set; }
public DbSet<Secretary> Secretarys { get; set; }
public DbSet<Disease> Diseases { get; set; }
public DbSet<Consultation> Consultations { get; set; }
public DbSet<Administrator> Administrators { get; set; }
}
The Users table was described like this:
public class User
{
[Key]
public Guid UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string IdentityCardNumber { get; set; }
public string PersonalNumericalCode { get; set; }
public DateTime DateOfBirth { get; set; }
public string Address { get; set; }
}
Next, I was asked to make sure that all the 'UserName' attributes would be unique. Since there is no annotation for that, I had to figure out a work-around. And here it is:
First, I changed my database context class to look like this:
public class ClinicDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Doctor> Doctors { get; set; }
public DbSet<Patient> Patients { get; set; }
public DbSet<Secretary> Secretarys { get; set; }
public DbSet<Disease> Diseases { get; set; }
public DbSet<Consultation> Consultations { get; set; }
public DbSet<Administrator> Administrators { get; set; }
public class Initializer : IDatabaseInitializer<ClinicDbContext>
{
public void InitializeDatabase(ClinicDbContext context)
{
if (!context.Database.Exists() || !context.Database.CompatibleWithModel(false))
{
if (context.Database.Exists())
{
context.Database.Delete();
}
context.Database.Create();
context.Database.ExecuteSqlCommand("CREATE INDEX IX_Users_UserName ON dbo.Users ( UserName )");
}
}
}
}
The important part from above is the sql command which alters the table by enforcing a unique index on our desired column -> UserName in our case.
This method can be called from the main class for example:
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<ClinicDbContext>(new ClinicDbContext.Initializer());
using (var ctx = new ClinicDbContext())
{
Console.WriteLine("{0} products exist in the database.", ctx.Users.Count());
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
The final issue, which occurred when trying to run the the Program class was the following: column in table is of a type that is invalid for use as a key column in an index
To solve this issue, I just added a [MaxLength(250)] annotation for the UserName attribute.
Here is how the User class looks in the end:
public class User
{
[Key]
public Guid UserId { get; set; }
[MaxLength(250)]
public string UserName { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string IdentityCardNumber { get; set; }
public string PersonalNumericalCode { get; set; }
public DateTime DateOfBirth { get; set; }
public string Address { get; set; }
}
Hope it will solve your problem too!
I've tried defining the following tables:
Orders [Id (primary, identity), ClientName, FriendlyOrderNum (unique)]
OrderItems [Id (primary, identity), FriendlyOrderNum (unique), ItemName]
And a foreign key mapping from OrderItems.FriendlyOrderNum (Mant) to Orders.FriendlyOrderNum (one).
If unique non-primary keys are possible the following SSDL should work:
<Schema Namespace="EfUkFk_DbModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="EfUkFk_DbModelStoreContainer">
<EntitySet Name="OrderItems" EntityType="EfUkFk_DbModel.Store.OrderItems" store:Type="Tables" Schema="dbo" />
<EntitySet Name="Orders" EntityType="EfUkFk_DbModel.Store.Orders" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="OrderItems">
<Key>
<PropertyRef Name="RowId" />
</Key>
<Property Name="RowId" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="OrderNum" Type="char" Nullable="false" MaxLength="5" />
<Property Name="ItemName" Type="varchar" MaxLength="100" />
</EntityType>
<!--Errors Found During Generation:
warning 6035: The relationship 'FK_OrderItems_Orders' has columns that are not part of the key of the table on the primary side of the relationship. The relationship was excluded.
-->
<EntityType Name="Orders">
<Key>
<PropertyRef Name="RowId" />
</Key>
<Property Name="RowId" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="ClientName" Type="varchar" MaxLength="100" />
<Property Name="OrderNum" Type="char" Nullable="false" MaxLength="5" />
</EntityType>
<!-- AsafR -->
<Association Name="FK_OrderItems_Orders">
<End Role="Orders" Type="EfUkFk_DbModel.Store.Orders" Multiplicity="1">
</End>
<End Role="OrderItems" Type="EfUkFk_DbModel.Store.OrderItems" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Orders">
<PropertyRef Name="OrderNum" />
</Principal>
<Dependent Role="OrderItems">
<PropertyRef Name="OrderNum" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema></edmx:StorageModels>
It doesn't. There's also no possibility for adding more <key> elements in an <EntityType>.
My conclusion is that non-primary unique keys are not support in EF 4.
You can use DataAnnotations validation as well.
I've created this (UniqueAttribute) class, that inherits ValidationAttribute, and when applied to a property, the values of that column will be retrieved and validated against, during validation.
You can grab the raw code from here.