Ok so here's some context: I'm using EF5, MVC4 and SQL CE4 to build up a web application. I've been loosely following this tutorial with a few differences.
My context class and POCO objects are in their own assembly.
I'm using SQL CE4 instead of SQL Express Local DB
My classes aren't as simple as the tutorial
I've already used a workaround to get simple classes to work register.
I had thought using enums in EF5 was supported in EF5, but can they be used in Keys?
When I try to add a control (Add Controller, MVC controller with read/write actions and views, using Entity Framework) for a simple class (1 int key property, 1 string property), it works.
I get varied errors when trying to add a class that has a property which is part of a key (primary or foreign)
Unable to retrieve metadata for 'blah'. Using the
same DbCompiledModel to create contexts against different types of
database servers is not supported. Instead, create a separate
DbCompiledModel for each type of server being used.
Index was out of range. Must be non-negative and less than the size of
the collection.
Parameter name: index
C:\Program Files (x86)\Microsoft Visual Studio\11.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 4\
CodeTemplates\AddController\ControllerWithContext.tt(0,0) : error :
Running transformation: System.IndexOutOfRangeException: Index was outside the bounds of the
array.
---StackTrace---
The only similarities I've found between these classes is that they have an emun that's tied to a key. Other classes with non-key enums generate correctly. Is this the issue or have I completely missed the mark?
Edit: Example of a class which fails
public class A
{
public virtual AIdEnum Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
public virtual ICollection<C> Cs { get; set; }
}
Ok, so I've just ran this up quickly with SQL CE 4 and it appears to work great:
public class EnumTestContext : DbContext
{
public EnumTestContext() : base("CEDB")
{
}
public DbSet<MyClass> MyClasses { get; set; }
}
public enum MyEnum
{
EnumValue1,
EnumValue2
}
public class MyClass
{
[Key, Column(Order = 0)]
public MyEnum MyEnumKey { get; set; }
[Key, Column(Order = 1)]
public int MyIntKey { get; set; }
[Column(Order = 2)]
public string Name { get; set; }
}
I then add an entity like this:
using (var context = new EnumTestContext())
{
context.MyClasses.Add(new MyClass()
{
MyEnumKey = MyEnum.EnumValue1,
MyIntKey = 22,
Name = "Hello World"
});
context.SaveChanges();
}
This is all working fine for me - does this help?
You need put this line:
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
before the DbContext lifecicle beggin.
A exemple that you can to download in the MSDN Gallery
It seems my issue is that Controller creation doesn't work with SQLCE 4.0 connectionStrings so using a conectionString of provider of System.Data.SqlClient handled that issue.
The next problem I had was that connectionString values (such as encryption) were not respected through this means so I now have 2 different constructors to get around this bug.
#if DEBUG
public Context()
: base("name=DefaultConnection")
{ ; }
#else
/// <summary>
/// Default Constructor
/// </summary>
public Context()
: base("name=CompactConnection")
{ ; }
#endif
Here's my configuration:
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.4.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
<connectionStrings>
<add name="CompactConnection" providerName="System.Data.SqlServerCe.4.0"
connectionString="Data Source="|DataDirectory|\DB.sdf";encryption mode=platform default;password="P4$$w0rd!""/>
<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MsSqlCe-20121028004432;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MsSqlCe-20121028004432.mdf" />
</connectionStrings>
Of course, this is just a workaround for a deeper issue. If anyone else knows of the root cause of this issue, I'd love to know.
Related
I just stumbled onto Audit.Net and I'm hooked. I went through the Audit.Net Entity Framework (6) documentation and am a bit lost in the output part.
My solution is a bit many-layers design:
Mvc 5 website
Wcf Client
Using WcfClientProxyGenerator
Wcf Service
Separate IService Contracts Library
All interfaces expose BDOs
Business Logic
Seperate BDO Library
Data Access
Reference EF6
Reference Audit.Net
DTOs
Entity Framwework Library
Has EDMX only
Reference Audit.Net
Reference: My EDMX is named Focus
Usage
I manage to modify the FocusModel.Context.tt from:
partial class <#=code.Escape(container)#> : DbContext
To:
partial class <#=code.Escape(container)#> : Audit.EntityFramework.AuditDbContext
Configuration
I found the default setting for Mode, IncludeEntityObjects, & AuditEventType were to my liking. the attribute for Include/Ignore entities/properties were straightforward as well.
Output
This is where I'm confused. I need to audit to the same database preferably to Audit tables for selected entities. Every entity in my database has composite PKs. How do I set the output mode in this scenario? Also, in my solution setup, the starting point for all projects that are behind the WCF Service is the WCF service itself. Does this mean that the point to Fluent-API-configure Audit.Net is here?
Have you seen the main Audit.NET documentation, specifically the output data providers?
I need to audit to the same database preferably to Audit tables for selected entities. Every entity in my database has composite PKs.
So you can use the EF data provider. It works with any kind of primary key.
How do I set the output mode in this scenario?
I'm not sure what do you mean by output mode, but I'm guessing you ask about OptIn/OptOut to ignore your audit entities to be audited. If that's the case you have multiple options, like using AuditIgnore attribute on your audit POCO classes, or via the fluent-api OptIn()/OptOut() methods. See example below.
The starting point for all projects that are behind the WCF Service is the WCF service itself. Does this mean that the point to Fluent-API-configure Audit.Net is here?
You can configure the Audit.NET library in any place, but you must do it before any audit event creation, so it is recommended to be on your startup code, as soon as your app or service starts.
Sample code
The following is a minimal example showing how you can configure the Audit.NET and Audit.EntityFramework libraries.
Suppose you have the following schema:
public class Student
{
public int PK_1 { get; set; }
public string PK_2 { get; set; }
public string Name { get; set; }
}
public class Student_Audit
{
public int PK_1 { get; set; }
public string PK_2 { get; set; }
public string Name { get; set; }
public DateTime AuditDate { get; set; }
public string AuditAction { get; set; }
}
public class SchoolContext : AuditDbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder) //<--Tip: its not DbModelBuilder, its Microsoft.EntityFrameworkCore.ModelBuilder
{
modelBuilder.Entity<Student>().HasKey(c => new { c.PK_1, c.PK_2 });
modelBuilder.Entity<Student_Audit>().HasKey(c => new { c.PK_1, c.PK_2, c.AuditDate });
}
public DbSet<Student> Students { get; set; }
public DbSet<Student_Audit> Students_Audit { get; set; }
}
You can configure the library on your startup code as follows:
// Setup audit to use the EF data provider
Audit.Core.Configuration.Setup()
.UseEntityFramework(_ => _
.AuditTypeExplicitMapper(m => m
// Map Student to Student_Audit
.Map<Student, Student_Audit>((ev, ent, studentAudit) =>
{
//add the action name and the date to the audit entity
studentAudit.AuditAction = ent.Action;
studentAudit.AuditDate = DateTime.UtcNow;
})));
// Configure the EF audit behavior
Audit.EntityFramework.Configuration.Setup()
.ForContext<SchoolContext>(_ => _.IncludeEntityObjects())
.UseOptOut()
.Ignore<Student_Audit>(); // Do not audit the audit tables
And a test case:
using (var db = new SchoolContext())
{
db.Database.EnsureCreated();
var st = new Student() { PK_1 = 1, PK_2 = "one", Name = "John" };
db.Students.Add(st);
db.SaveChanges();
}
Will generate the following:
I'm try to use PetaPoco with SQLite on MVC5. I created a model to make the mapping.
namespace LittleDemo.Models.UserModel
{
[PetaPoco.TableName("User")]
[PetaPoco.PrimaryKey("UserId", AutoIncrement = true)]
public class User
{
[Column]
public int UserId { get; set; }
[Column]
public string Name { get; set; }
}
}
Then I create a controller file
public ActionResult UserView()
{
ViewBag.Message = "Your User page.";
var dbUser = new PetaPoco.Database("sqlite");
var userA = new User {Name = "A"};
dbUser.Save(userA);
return View();
}
create the database and put some data in. then create a view to show all the list of the data. but it returns a error message "no user table found".
How can I assign the table with PetaPoco.
I have invested a lot of time to make it work but I found nothing.
You could achieve same using https://github.com/schotime/NPoco
Nuget available here https://www.nuget.org/packages/NPoco/
You need to do some work around to make it work.
open app.config/web.config
Step 1:
install https://www.nuget.org/packages/NPoco/
Step 2:
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite.EF6" />
<add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
<remove invariant="System.Data.SQLite" /><add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>
Step 3:
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source = C:\db\test.db;" providerName="System.Data.SQLite" />
</connectionStrings>
Step 4:
public class User
{
public int Id { get; set; }
public string Email { get; set; }
}
static void Main(string[] args)
{
var config = ConfigurationManager.ConnectionStrings["DefaultConnection"];
var db = new MyDb(config.ConnectionString, config.ProviderName);
List<User> users = db.Fetch<User>(string.Empty);
Console.WriteLine(users.Count);
}
public class MyDb : NPoco.Database
{
public MyDb()
: base("DefaultConnection")
{
}
public MyDb(string connectionString, string providerName)
: base(connectionString, providerName)
{
}
}
Reference :
https://github.com/venkata-ramana/SQLiteSample
Let me know if you have any queries, Thank you.
This has been addressed in multiple other questions, but alas, I have tried all the solutions posted there with no success. I'm developing an ASP.NET MVC application, using Code-First EF. I am trying to take advantage of the scaffolding built in so that it can automatically create a Controller for me based off my Model and DbContext. However, I am getting the following error when I try to create a Controller in this way:
'Unable to retrieve metadata for Employer.' Using the same DbCompiledModel to create contexts against different types of database servers is not supported. Instead, create a separate DbCompiledModel for each type of server being used.
The code for my model, Employer, my DbContext, MyDataContext, and my web.config file follow:
//Employer.cs
public class Employer : Organization, IEmployer
{
public virtual PhysicalAddress Address { get; set; }
public virtual ICollection<ContactMethod> ContactMethods { get; set; }
public int FederalTaxID { get; set; }
public virtual Client Client { get; set; }
}
-
//MyDataContext.cs
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class MyDataContext : IdentityDbContext<ApplicationUser>
{
public MyDataContext()
: base("DataConnection")
{
}
static MyDataContext()
{
Database.SetInitializer(new DataInitializer());
}
public DbSet<Client> Clients { get; set; }
public DbSet<Organization> Organizations { get; set; } // Store Employer and OrgEntity
/// <summary>
/// Sets up unclear relationships between entities before the models are constructued in a database.
/// For example, models which extend other models must have their Ids mapped (because it is an
/// inherited member, and so is not found by Entity Framework by default)
/// </summary>
/// <param name="modelBuilder">the object responsible for constructing the database from code-first</param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//set up parent organization relationship
modelBuilder.Entity<Organization>()
.HasOptional(n => n.ParentOrganization)
.WithMany(o => o.ChildOrganizations)
.Map(m => m.MapKey("ParentOrganization_Id"));
//set up the organization-employee assignment
modelBuilder.Entity<Employee>()
.HasOptional(a => a.OrganizationAssignment)
.WithMany(a => a.Employees);
//used to integrate Identity stuff into same db as Clients, etc.
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}
}
-
<!--web.config-->
.
.
.
<connectionStrings>
<add name="DataConnection"
providerName="Mysql.Data.MysqlClient"
connectionString="server=localhost;user id=myid;password=mypass;persistsecurityinfo=True;database=app_db"/>
</connectionStrings>
.
.
.
From various other posts like this one , this one , and this one . I have tried many different things to get it to work. This includes commenting out my constructor for MyDbContext, changing the providerName attribute of my connection string to be "System.Data.SqlClient", changing the connection string name to be "DefaultConnection", removing my connection strings altogether, and combinations of all of these things. I make sure to rebuild between trying to add the Controller. However, after performing these different changes, I still receive the same error when I try to add a new Controller.
Generally, I can find all my questions already answered, but the answers don't seem to be working for me on this one. I think what might separate my case from the ones linked is that my DbContext is actually an instance of IdentityDbContext. I believe the constructor for IdentityDbContext just calls the base constructor for it, anyway, so I don't see how this could be much of an issue.
Any help is much appreciated. Thank you!
You didn't map your Employer entity to the model. That's why compiler is unable to retrieve metadata for Employer. Firstly, add DbSet as below and let me know, if does it work.
public DbSet<Client> Clients { get; set; }
public DbSet<Organization> Organizations { get; set; }
public DbSet<Employer> Employers { get; set; }
I am trying to seed users and roles into my database.
Currently using Code First Entity Framework with Automatic Migrations in C# MVC4.
Whenever I call
Update-Database -Force
I get the following error:
Running Seed method.
System.InvalidOperationException: You must call the
"WebSecurity.InitializeDatabaseConnection" method before you call any
other method of the "WebSecurity" class. This call should be placed in
an _AppStart.cshtml file in the root of your site.
at WebMatrix.WebData.SimpleRoleProvider.get_PreviousProvider()
at WebMatrix.WebData.SimpleRoleProvider.RoleExists(String roleName)
at System.Web.Security.Roles.RoleExists(String roleName)
at GratifyGaming.Domain.Migrations.Configuration.Seed(GratifyGamingContext
context) in C:\Users\Unreal\Documents\Visual Studio
2010\Projects\GratifyGaming\GratifyGaming.Domain\Migrations\Configuration.cs:line
36
at System.Data.Entity.Migrations.DbMigrationsConfiguration1.OnSeed(DbContext
context)
at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable1
pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1
pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String
targetMigration)
at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class.
This call should be placed in an _AppStart.cshtml file in the root of
your site.
The offending line of code is the Role.Exists
I have tried putting the WebSecurity.InitializeDatabaseConnection in Global.asax, Seed(), and created an _AppStart.cshtml with no success. I have trawled the internet looking for a possible solution and none of them have worked (including other stack overflow articles). Some notable blog posts are below.
http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/
http://odetocode.com/Blogs/scott/archive/2012/08/31/seeding-membership-amp-roles-in-asp-net-mvc-4.aspx
See code below.
[Configuration.cs]
protected override void Seed(GratifyGaming.Domain.Models.DAL.GratifyGamingContext context)
{
var criteria = new List<Criterion>
{
new Criterion { ID = 1, IsMandatory=true, Name = "Gameplay", Description="The playability of the games core mechanics" },
new Criterion { ID = 2, IsMandatory=true, Name = "Art Style", Description="The artistic feel of the game as a whole. Elements such as story, style and originality come into play." },
new Criterion { ID = 3, IsMandatory=true, Name = "Longevity", Description="How long did this game keep you entertained?" },
new Criterion { ID = 4, IsMandatory=true, Name = "Graphics", Description="How good does the game look?" }
};
criteria.ForEach(s => context.Criterion.AddOrUpdate(s));
context.SaveChanges();
if (!Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
if (!WebSecurity.UserExists("user"))
WebSecurity.CreateUserAndAccount(
"user",
"password");
if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
Roles.AddUsersToRoles(new[] { "user" }, new[] { "Administrator" });
}
The Criterion seed code works without fail.
[_AppStart.cshtml]
#{
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId",
"UserName", autoCreateTables: true);
}
}
Normal login to my site works perfectly with this located here.
[web.config]
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
[AccountModel.cs]
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public virtual ICollection<Game> AttachedGames { get; set; }
public virtual ICollection<UserGratificationRecord> GratificationHistory { get; set; }
[ForeignKey("UserLevel")]
public int? AcheivementID { get; set; }
public virtual Acheivement UserLevel { get; set; }
public int? NumOfGratifictions { get; set; }
}
UPDATE
I think the WebSecurity.InitializeDatabaseConnection is not even being run - I can place more than one in my Seed method, and not get the 'can only be called once' error that you would normally get.
My seed method is in my domain project along with all the models, while everything else is in a WebUI project. Not sure if this has anything to do with it.
Just put that lazy init into the top of your Seed method
protected override void Seed(GratifyGaming.Domain.Models.DAL.GratifyGamingContext context)
{
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile",
"UserId",
"UserName",
autoCreateTables: true);
}
In your App_Start, try adding:
var configuration = new Data.Migrations.Configuration();
var migrator = new DbMigrator(configuration);
migrator.Update();
You will have to make your configuration.cs file public
public class Configuration : DbMigrationsConfigurati
This should make your seed method get called when you run your program
Delete your existing reference to WebMatrix.WebData
Add reference WebMatrix.WebData version 2.
Error will stop.
How do I tell EF what to name the database and where to put it?
If there is no connection string in the Web.Config, it tries to put it in the local SQLEXPRESS Server, but I want to put it out on a known SQL Server and name it what I want. Any suggestions?
Create a connection string in the app.config/web.config with the same name as the context and the EF will use that DB.
How to Use a Different Connection String Name with EF
EF will use the name of the database in the connection string. When you want to decouple the name of your connection string from EF, you need to provide your connection string to the constructor. Example:
public class DatabaseContext : DbContext
{
public DatabaseContext()
: base(ApplicationParameters.ConnectionStringName)
{
}
public DatabaseContext(string connectionStringName)
: base(connectionStringName)
{
}
}
in Class :
public class Context : DbContext
{
//SET CONNECTION STRING NAME FOR DataBase Name :
public Context() : base("YourConnectionName") { }
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
in web.config:
<connectionStrings>
<add name="YourConnectionName" connectionString="Data Source=A-PC\SQLEXPRESS;
Initial Catalog=MyDataBase; Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
Thanks ferventcoder.
Ref => http://brandonclapp.com/connection-strings-with-entity-framework-5-code-first/
Alternatively you can set the name in your DbContext constructor.
As already mentioned, you can declare your connection string inside the config file of your application with a name (let's say "YourDBName") and then pass this to the DbContext base constructor call (I will add this to the answer for providing a complete answer - great answers already given on this).
Alternatively, you can set this programmatically in your DbContext Extension class, using the Database.Connection.ConnectionString property. For instance:
App.config:
<!-- More.... -->
<!-- You can do this in a declarative way -->
<connectionStrings>
<add name="YourDBName"
connectionString="<Your connection string here>"
providerName="<Your provider here>" />
</connectionStrings>
<!-- More.... -->
DatabaseContext.cs:
public class DatabaseContext : DbContext
//Link it with your config file
public DatabaseContext () : base("YourDBName")
{
//And/Or you can do this programmatically.
this.Database.Connection.ConnectionString = "<Your Connection String Here>";
// More Stuff.....
}
}
If you point your connection-string at an existing database then EF “code first” will not try and create one automatically.
EF “code first” uses a convention where context classes by default look for a connection-string that has the same name as the context class.
Using ef code first with an existing database
For reference, here is how to do it in code using VB.NET:
Public Class DatabaseContext : Inherits DbContext
Public Property Users As DbSet(Of User)
Public Sub New()
MyBase.New("NewFileName.sdf")
End Sub
End Class
You can specify the name of the connection string the context constructor:
public YourDbContext()
: base("Name=YourDbContext")
{
}