Getting code-first Entity Framework to build tables on SQL Azure - asp.net-mvc-3

I am new the code-first Entity Framework.
I have tried a few things now, but can't get EF to construct any tables in the my SQL Azure database.
Can anyone advise of some steps and settings I should check.
The membership provider has no problems create it's tables.
I have added the PersistSecurityInfo=True in the connection string. The connection string is using the main user account for the server.
When I implement the tables in the database using sql everything works fine.
I have the following in the WebRole.cs
//Initialize the database
Database.SetInitializer<ReykerSCPContext>(new DbInitializer());
My DbInitializer (which does not get run before I get a "Invalid object name 'dbo.ClientAccountIFAs'." when I try to access the table for the first time. Sometime after startup.
public class DbInitializer:DropCreateDatabaseIfModelChanges<ReykerSCPContext>
{
protected override void Seed(ReykerSCPContext context)
{
using (context)
{
//Add Doc Types
context.DocTypes.Add(new DocType() { DocTypeId = 1, Description = "Statement" });
context.DocTypes.Add(new DocType() { DocTypeId = 2, Description = "Contract note" });
context.DocTypes.Add(new DocType() { DocTypeId = 3, Description = "Notification" });
context.DocTypes.Add(new DocType() { DocTypeId = 4, Description = "Invoice" });
context.DocTypes.Add(new DocType() { DocTypeId = 5, Description = "Document" });
context.DocTypes.Add(new DocType() { DocTypeId = 6, Description = "Newsletter" });
context.DocTypes.Add(new DocType() { DocTypeId = 7, Description = "Terms and Conditions" });
//Add ReykerAccounttypes
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 1, Description = "ISA" });
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 2, Description = "Trading" });
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 3, Description = "SIPP" });
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 4, Description = "CTF" });
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 5, Description = "JISA" });
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 6, Description = "Direct" });
context.ReykerAccountTypes.Add(new ReykerAccountType() { ReykerAccountTypeID = 7, Description = "ISA & Direct" });
//Save the changes
base.Seed();
}
and my DBContext class looks like
public class ReykerSCPContext : DbContext
{
//set the connection explicitly
public ReykerSCPContext():base("ReykerSCPContext"){}
//define tables
public DbSet<ClientAccountIFA> ClientAccountIFAs { get; set; }
public DbSet<Document> Documents { get; set; }
public DbSet<DocType> DocTypes { get; set; }
public DbSet<ReykerAccountType> ReykerAccountTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Runs when creating the model. Can use to define special relationships, such as many-to-many.
}
The code used to access the is
public List<ClientAccountIFA> GetAllClientAccountIFAs()
{
using (DataContext)
{
var caiCollection = from c in DataContext.ClientAccountIFAs
select c;
return caiCollection.ToList();
}
}
and it errors on the last line.
Help!

The problem here transpired to be mixed up with the Universal Membership Provider I was using.
The membership provider was instantiating the database before Entity Framework, so that each time EF thought the database existed and did not try to drop it.
To solve this I made sure that not only did I set the initializer in the OnStart method of the WebRole.cs, but to also force a query to the DbContext too.
This then forced the database to be created prior to the membership tables and all was well.
Once the membership provider is called for the first time, it creates the tables that are necessary.

Related

Unable to bind data to Kendo Scheduler

I've got this Kendo Scheduler that is displayed in the View but without any data.
The Scheduler on the View:
#(Html.Kendo().Scheduler<ProjName.Models.ScheduleInspectionModel>()
.Name("scheduler")
.Views(views =>
{
views.DayView();
views.WorkWeekView();
views.WeekView();
views.MonthView(mv => mv.Selected(true));
views.AgendaView();
})
.Timezone("Etc/UTC")
.DataSource(d => d
.Read("ControllerName", "GetScheduleInspections")
)
)
The datasource invokes the controller method below:
public ActionResult GetScheduleInspections([DataSourceRequest]DataSourceRequest request)
{
ScheduleInspectionModel sim = new ScheduleInspectionModel();
var gsio = sim.getScheduleInspections();
List<ScheduleInspectionModel> list = new List<ScheduleInspectionModel>();
if (gsio.scheduleinspections != null)
{
foreach (wsScheduleInspection.scheduleInspectionOutput scheduleInspection in gsio.scheduleinspections)
{
ScheduleInspectionModel sim2 = new ScheduleInspectionModel
{
GlobalEquipConditionId = scheduleInspection.globalEquipmentCondition.id,
Description = scheduleInspection.globalEquipmentCondition.code,
Start = DateTime.Now,
End = DateTime.Now.AddHours(2),
Title = scheduleInspection.globalEquipmentCondition.code,
IsAllDay = true
};
list.Add(sim2);
}
}
return Json(list.ToDataSourceResult(request));
}
But this method is never run, despite being on the Scheduler Datasource property. It should run that method and return a list of inspections. I don't know why isn't the method being hit. With a Kendo Grid, for example, the method on the Datasource Read is hit as soon as the page is loaded.
Try making sure your definition has these two items as I think they are required.
.Date(new DateTime(2013, 6, 13))
.StartTime(new DateTime(2013, 6, 13, 7, 00, 00))
EDIT
I was able to get the following code to work:
Model
// NOTE: It's important that your model class implements ISchedulerEvent
public class TaskViewModel : ISchedulerEvent
{
public string Title { get; set; }
public string Description { get; set; }
public bool IsAllDay { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string StartTimezone { get; set; }
public string EndTimezone { get; set; }
public string RecurrenceRule { get; set; }
public string RecurrenceException { get; set; }
}
SchedulerController.cs
public class SchedulerController : Controller
{
// GET: Scheduler
public ActionResult Index()
{
var model = new SchedulerViewModel();
// In this case, it doesn't matter what this model is really since we're using AJAX binding
return View(model);
}
// I usually have my binding methods for Kendo use HttpPost
[HttpPost]
public ActionResult GetData([DataSourceRequest] DataSourceRequest request)
{
var data = new List<TaskViewModel>
{
new TaskViewModel
{
Start = new DateTime(2014, 12, 1, 8, 0, 0),
End = new DateTime(2014, 12, 1, 17, 0, 0),
Title = "Task 1"
}
};
return Json(data.ToDataSourceResult(request));
}
}
Index.cshtml (view)
#(Html.Kendo().Scheduler<TaskViewModel>()
.Name("scheduler")
.Views(views =>
{
views.DayView();
views.WorkWeekView();
views.WeekView();
views.MonthView(mv => mv.Selected(true));
views.AgendaView();
})
.Timezone("Etc/UTC")
.DataSource(d => d
.Read("GetData", "Scheduler")
))
If this doesn't work for you, I would make sure your versions (for Kendo, jQuery, etc) are correct. Hope this helps.
Yes, Scheduler read is called as soon as it is loeded. But it may not be getting data in proper format as it expects. So it is not able to bind the data. If you can check for these modification:
1) Define the "Model" in "DataSource" of scheduler as defined in this example.
2) Also the action method should return object of "MyModel" class(model on which scheduler is defined)
not "ScheduleInspectionModel" class.

How to get ASP.Net Web API and OData to bind a string value as a key?

I'm going through a short Web Api + OData tutorial from asp.net: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/getting-started-with-odata-in-web-api/create-a-read-only-odata-endpoint.
I downloaded the example project, and it works. But then I started playing around with the Product model that they use in the example. I added a new property to act as a key of type string instead of an integer key.
The new Product.cs:
public class Product
{
public string stringKey { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
The modified controller:
public class ProductsController : EntitySetController<Product, string>
{
static List<Product> products = new List<Product>()
{
new Product() { stringKey = "one", ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
new Product() { stringKey = "two", ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
new Product() { stringKey = "three", ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
new Product() { stringKey = "four", ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
new Product() { stringKey = "five", ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
};
[Queryable]
public override IQueryable<Product> Get()
{
return products.AsQueryable();
}
protected override Product GetEntityByKey(string key)
{
return products.FirstOrDefault(p => p.stringKey == key);
}
}
The trouble is that when I go to /odata/Products(one) the string "one" is not bound to the key argument in the GetEntityByKey(string key) action. However, when I browse to odata/Products(1) then "1" does get bound to the key argument.
How can I get a string with text values to bind correctly, instead of just binding strings with numerical values?
Update
I forgot to include the WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
}
}
I noticed that the path /odata/Products(0011-1100) would only bind "0011" as the string key.
After some playing around with it, I've found that the following path works as I had hoped:
/odata/Products('one')
It appears the single quotes are required to read the entire string within the parentheses.

Getting an Enum to display on client side

I'm having hard time understanding how to convert an Enum value to it's corresponding name. My model is as follows:
public class CatalogRule
{
public int ID { get; set; }
[Display(Name = "Catalog"), Required]
public int CatalogID { get; set; }
[Display(Name = "Item Rule"), Required]
public ItemType ItemRule { get; set; }
public string Items { get; set; }
[Display(Name = "Price Rule"), Required]
public PriceType PriceRule { get; set; }
[Display(Name = "Value"), Column(TypeName = "MONEY")]
public decimal PriceValue { get; set; }
[Display(Name = "Exclusive?")]
public bool Exclude { get; set; }
}
public enum ItemType
{
Catalog,
Category,
Group,
Item
}
public enum PriceType
{
Catalog,
Price_A,
Price_B,
Price_C
}
A sample result from .net API:
[
{
$id: "1",
$type: "XYZ.CMgr.Models.CatalogRule, XYZ.CMgr",
ID: 1,
CatalogID: 501981,
ItemRule: 0,
Items: "198",
PriceRule: 1,
PriceValue: 0.5,
Exclude: false
},
{
$id: "2",
$type: "XYZ.CMgr.Models.CatalogRule, XYZ.CMgr",
ID: 2,
CatalogID: 501981,
ItemRule: 2,
Items: "9899",
PriceRule: 2,
PriceValue: 10.45,
Exclude: false
}
]
So in this example, I need to get Catalog for results[0].ItemRule & Price A for results[0].PriceRule. How can I accomplish this in BreezeJS??
This is easy to do in ASP.NET Web API, because it is an out-of-box feature in the default JSON serializer (Json.NET).
To see strings instead of enum numbers in JSON, just add an instance of StringEnumConverter to JSON serializer settings during app init:
var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
UPDATE: Yep, you right, this is not help with Breeze.js. Ok, you can anyway do a little magic to make enums work like strings (while new version with fix is not released).
Create a custom ContextProvider which updates all integer enum values in metadata to strings. Here it is:
public class StringEnumEFContextProvider<T> : EFContextProvider<T>
where T : class, new()
{
protected override string BuildJsonMetadata()
{
XDocument xDoc;
if (Context is DbContext)
{
xDoc = GetCsdlFromDbContext(Context);
}
else
{
xDoc = GetCsdlFromObjectContext(Context);
}
var schemaNs = "http://schemas.microsoft.com/ado/2009/11/edm";
foreach (var enumType in xDoc.Descendants(XName.Get("EnumType", schemaNs)))
{
foreach (var member in enumType.Elements(XName.Get("Member", schemaNs)))
{
member.Attribute("Value").Value = member.Attribute("Name").Value;
}
}
return CsdlToJson(xDoc);
}
}
And use it instead of EFContextProvider in your Web API controllers:
private EFContextProvider<BreezeSampleContext> _contextProvider =
new StringEnumEFContextProvider<BreezeSampleContext>();
This works well for me with current Breeze.js version (1.1.3), although I haven't checked other scenarios, like validation...
UPDATE: To fix validation, change data type for enums in breeze.[min|debug].js, manually (DataType.fromEdmDataType function, dt = DataType.String; for enum) or replace default function during app init:
breeze.DataType.fromEdmDataType = function (typeName) {
var dt = null;
var parts = typeName.split(".");
if (parts.length > 1) {
var simpleName = parts[1];
if (simpleName === "image") {
// hack
dt = DataType.Byte;
} else if (parts.length == 2) {
dt = DataType.fromName(simpleName);
if (!dt) {
if (simpleName === "DateTimeOffset") {
dt = DataType.DateTime;
} else {
dt = DataType.Undefined;
}
}
} else {
// enum
dt = DataType.String; // THIS IS A FIX!
}
}
return dt;
};
Dirty, dirty hacks, I know... But that's the solution I found
There will be a new release out in the next few days where we "change" breeze's enum behavior ( i.e. break existing code with regards to enums). In the new release enums are serialized and queried by their .NET names instead of as integers. I will post back here when the new release is out.

Using sample data to seed database for Entity Framework doesn't create database

I'm pretty new to using Entity Framework, as well as to using ASP.NET MVC. I've been able to complete a couple of tutorials on the ASP.NET MVC website and thought I would start my own project based on the MVC Music Store application.
I can't tell where I went wrong (it all looks the same to me), but for some reason my seed data isn't creating the database.
Item class:
namespace MySite.Models
{
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Tag class:
namespace MySite.Models
{
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
}
}
ItemTag class:
namespace MySite.Models
{
public class ItemTag
{
public int Id { get; set; }
public int ItemId { get; set; }
public int TagId { get; set; }
}
}
MySiteEntities class:
namespace MySite.Models
{
public class MySiteEntities : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<ItemTag> ItemTags { get; set; }
}
}
Global.asax class:
namespace MySite
{
public class MvcApplication : System.Web.HttpApplication
{
...
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(new MySite.Models.SampleData());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
Web.config file:
<configuration>
...
<connectionStrings>
<add name="MySiteEntities"
connectionString="Data Source=|DataDirectory|MySite.sdf"
providerName="System.Data.SqlServerCe.4.0" />
</connectionStrings>
</configuration>
SampleData class:
namespace MySite.Models
{
public class SampleData : DropCreateDatabaseIfModelChanges<MySiteEntities>
{
protected override void Seed(MySiteEntities context)
{
new List<Item>
{
new Item { Id = 1, Name = "Bob Jones" },
new Item { Id = 2, Name = "George Smith" },
new Item { Id = 3, Name = "Boys and Girls" },
new Item { Id = 4, Name = "The President's Hair" },
new Item { Id = 5, Name = "Invaders From Mars" },
new Item { Id = 6, Name = "Tank Shooter" },
new Item { Id = 7, Name = "Stew's Blog" },
new Item { Id = 8, Name = "Social Mania" }
}.ForEach(i => context.Items.Add(i));
new List<Tag>
{
new Tag { Id = 1, Name = "Author" },
new Tag { Id = 2, Name = "Movie" },
new Tag { Id = 3, Name = "Video Game" },
new Tag { Id = 4, Name = "Website" }
}.ForEach(t => context.Tags.Add(t));
new List<ItemTag>
{
new ItemTag { Id = 1, ItemId = 1, TagId = 1 },
new ItemTag { Id = 2, ItemId = 2, TagId = 1 },
new ItemTag { Id = 3, ItemId = 3, TagId = 2 },
new ItemTag { Id = 4, ItemId = 4, TagId = 2 },
new ItemTag { Id = 5, ItemId = 5, TagId = 3 },
new ItemTag { Id = 6, ItemId = 6, TagId = 3 },
new ItemTag { Id = 7, ItemId = 7, TagId = 4 },
new ItemTag { Id = 8, ItemId = 8, TagId = 4 }
}.ForEach(x => context.ItemTags.Add(x));
}
}
}
That's pretty much everything. I did delete the database file at one point. Shouldn't it get created again when I build the solution?
Reason: This is because you are using "DropCreateDatabaseIfModelChanges".
As it indicates if your model doesn't change then it wont create the database (or if the DB is already there then it wont add your populated data).
Solution: You want to use "CreateDatabaseIfNotExists" instead.
Alternative: If you're DB is already created and you just want to add initial data then use "DropCreateDatabaseAlways" HOWEVER you must realise this will recreate the DB from scratch every time an application restarts. So only use this when your DB is already created and you don't want to change the model (and you don't care about losing the data already in the DB) AND then change it to one of the other two options.
I see you're adding your objects to your entity, but are you calling context.SaveChanges() to commit then to the db?
found a link that may help: EF4 Code First CTP5: Seed method no longer works
Apparently the database doesn't get re-created until the page is refreshed in the browser. I kept re-building the solution, but wasn't refreshing the page.

Entity Framework Code First and populating join tables

I been practicing with EF Code First, SQL Express, and ASP.Net MVC3.
When I run the website first the correct tables are generated by the FooInitializer and Student and Image are populated but for some reason the join table (StudentImages) is not being populated.
What could be the issue?
Tables: Student, Image, and StudentImages
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Image> Images { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Filename { get; set; }
public string Extension { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class FooInitializer : DropCreateDatabaseIfModelChanges<DBContext>
{
protected override void Seed(DBContext context)
{
var students = new List<Student> {
new Student { Id = 1, Name = "John" },
new Student { Id = 2, Name = "Jane" }
};
students.ForEach(s => context.Students.Add(s));
context.SaveChanges();
var images = new List<Image> {
new Image { Id = 1, Filename = "IMG_4596.JPG", Extension = ".jpg" },
new Image { Id = 2, Filename = "IMG_4600.JPG", Extension = ".jpg" }
};
images.ForEach(i => context.Images.Add(i));
students[0].Images.Add(images[0]);
context.SaveChanges();
}
}
From what I can tell your Image class does not have a reference to the StudentID. Try adding:
public int StudentID { get; set; }
to the Image class maybe?
Also having an ICollection would mean that one image could have multiple students - is this correct? Maybe it should be a public virtual Student Student {...}
EDIT: Also I found this, with a many to many relationship (if thats what you need):
In your OnModelCreating() Method:
modelBuilder.Entity<Student>()
.HasMany(c => c.Images).WithMany(i => i.Students)
.Map(t => t.MapLeftKey("StudentId")
.MapRightKey("ImageID")
.ToTable("StudentImages"));
taken from this link that states:
A many-to-many relationship between the Instructor and Course
entities. The code specifies the table and column names for the join
table. Code First can configure the many-to-many relationship for you
without this code, but if you don't call it, you will get default
names such as InstructorInstructorID for the InstructorID column.
EDIT: Here is the code I used the other night, with my implementation of the code first MVC site:
var users = new List<User>
{
new User { UserID = new Guid(), Email = "me#me.com", LastOnline = DateTime.Now, Password = "pword", RegistrationDate = DateTime.Now, SecurityAnswer = "me", SecurityQuestion = "who?", Roles = new List<Role>() },
};
users.ForEach(s => context.Users.Add(s));
context.SaveChanges();
var roles = new List<Role>
{
new Role { RoleID = "Admin", Description = "Administration Users", Users = new List<User>() }
};
roles.ForEach(r => context.Roles.Add(r));
users[0].Roles.Add(roles[0]);
context.SaveChanges();
var userLicense = new List<UserLicense>
{
new UserLicense { AddDateTime = DateTime.Now, LicenseType = "Farmer", Manufacturer = "Motorola", Model = "Droid", PhoneIdentifier = "c0e4223a910f", UserID = users[0].UserID, User = new User() }
};
userLicense[0].User = users[0];
userLicense.ForEach(u => context.UserLicenses.Add(u));
context.SaveChanges();
userLicense[0].User = users[0];
context.SaveChanges();
Notice in each instantiated item, I am also instantiating a new referenced item within the parent object.
EDIT:
Ok try this:
var students = new List<Student> {
new Student { Id = 1, Name = "John", Images = new List<Image>() },
new Student { Id = 2, Name = "Jane", Images = new List<Image>() }
};
students.ForEach(s => context.Students.Add(s));
context.SaveChanges();
var images = new List<Image> {
new Image { Id = 1, Filename = "IMG_4596.JPG", Extension = ".jpg", Students = new List<Student>() },
new Image { Id = 2, Filename = "IMG_4600.JPG", Extension = ".jpg", Students = new List<Student>() }
};
images.ForEach(i => context.Images.Add(i));
students[0].Images.Add(images[0]);
students[1].Images.Add(images[1]);
context.SaveChanges();
Try adding this before saving changes for each student:
foreach (Image i in s1.Images)
context.ObjectStateManager.ChangeObjectState(i, System.Data.EntityState.Added);
Also try with System.Data.EntityState.Modified.
Hope this works...

Resources