Using sample data to seed database for Entity Framework doesn't create database - asp.net-mvc-3

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.

Related

Error in Linq Expression GroupBy in .NET Core 3.1

After migrating the code from .NET Core 2.1 into 3.1 the following Linq is not working . it shows the InvalidOperationException in LinqExpression - GroupByShaperExpression
Message=The LINQ expression '(GroupByShaperExpression:
KeySelector: new {
DepotNo = (g.DepotNo),
DepotName = (g.DepotName)
},
ElementSelector:(EntityShaperExpression:
EntityType: DepartmentWorkTime
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False
)
)
.Select(dd => new {
id = (object)dd.DepotNo + "." + (object)dd.DepartmentID,
title = (object)dd.Depot.DepotNo + "." + dd.Department.DepartmentName
})' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
I have two model and another view model
Depot Model
public class Depot
{
[Key]
public int DepotNo { get; set; }
public string DepotName {get;set;}
` }
public class Department
{
[Key]
public int DepartmentID{ get; set; }
public string DepartmentName {get;set;}
}
public class DepartmentWorkTime
{
[Key]
public int Id { get; set; }
public int DepotNo { get; set; }
public int DepartmentID { get; set; }
public DepotModel Depot { get; set; }
public DepartmentModel Department { get; set; }
}
I have the following record in Depot model and Department model
Depot Model
{1, .AAAA Depot},
{2, BBBB Depot},
{4, CCCC Depot},
Department Model
{1, Retail},
{2, Office},
{3, Field Staff},
{4, Warehouse},
DepartmentWorkTime List
{1,1,1, Depot Model ,Department Model},
{2,1,2, Depot Model ,Department Model},
{3,1,4, Depot Model ,Department Model},
{4,2,1, Depot Model ,Department Model},
{5,2,2, Depot Model ,Department Model},
{6,2,3, Depot Model ,Department Model},
{7,4,1, Depot Model ,Department Model},
I am trying to get the result from linq as
[0] = { id = 1, title = "1-AAAA Depot", subs = {System.Collections.Generic.List<<>f__AnonymousType10<string, string>>} }
Subs
[0] { id = "1.1", title = "1.Retail" } <Anonymous Type>
[1] { id = "1.2", title = "1.Office" } <Anonymous Type>
[2] { id = "1.4", title = "1.Warehouse" } <Anonymous Type>
[1] = { id = 2, title = "2-BBBB Depot", subs = {System.Collections.Generic.List<<>f__AnonymousType10<string, string>>} }
Subs
[0] { id = "2.1", title = "2.Retail" } <Anonymous Type>
[1] { id = "2.2", title = "2.Office" } <Anonymous Type>
[2] { id = "2.3", title = "2.Field Staff" } <Anonymous Type>
[2] ={ id = 3, title = "4-CCCC Depot", subs = {System.Collections.Generic.List<<>f__AnonymousType10<string, string>>} }
Subs
[0] { id = "4.1", title = "4.Retail" } <Anonymous Type>
For that purpose I have written the linq as given below , it is working in .Net Core 2.1 but not 3.1
**public JsonResult GetDepotDepartemntsForMap()
{
dynamic mappingList = new List<DepotMapModel>();
mappingList = _unitOfWork.Department.GetDepotWithDepartment();
return Json(mappingList);
}
public class DepartmentMapModel
{
public string id { get; set; }
public string title { get; set; }
}
public class DepotMapModel
{
public string id { get; set; }
public string title { get; set; }
public List<DepartmentMapModel> subs { get; set; }
}
public dynamic GetDepotWithDepartment()
{
var list = goContext.goDepartmentWorkTime.
GroupBy(d => new { d.DepotNo, d.Depot.DepotName })
.Select(g => new
{
id = g.Key.DepotNo,
title = g.Key.DepotName,
subs = g.Select(dd => new
{
id = dd.DepotNo + "." + dd.DepartmentID,
title = dd.Depot.DepotNo + "." + dd.Department.DepartmentName
}).ToList()
}).ToList();
return list;
}**
in the new EF core version, it only allows the last Select() call to be evaluated on the client.
You can get all the data by calling goDepartmentWorkTime, so you do not need to use groupby, check this:
var list = (from d in _context.goDepartmentWorkTime
select new
{
id = d.DepotNo,
title = d.Depot.DepotName,
subs = new
{
id = d.DepotNo + "." + d.DepartmentID,
title = d.Depot.DepotNo + "." + d.Department.DepartmentName
}
}).ToList();
Result:

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.

Getting code-first Entity Framework to build tables on SQL Azure

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.

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