ASP.NET MVC 5 with EF6 and MySql using Identity 2. I've been doing DBA-like 'bigdata' for quite a few years and now I'm back in the land of code. I understand EF as an architecture but am struggling with my lack of expertise in getting the ground floor laid for my project especially with the MySql wrinkle.
What I have done successfully: Created a project with a MySQLConfiguration, MySqlHistoryContext, and MySqlInitializer, ran a migration to successfully create the tables for Identity, deployed the site to local server and can successfully register a user and log in. GREAT!
Question: I'm lost as far as how to manage two contexts, merge contexts, what ought be my best practice so I can continue to extend Identity objects and add new database tables through POCO models and migrations to the same database and have them work through a context at the controller.
Guidance would be helpful. I believe my issues are in creating the second context which I would prefer use the same connection.
Attached are my class mods from other tutorials and references.
How do I create a new context which would create a new table from a POCO model using a migration??
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
static ApplicationDbContext()
{
Database.SetInitializer(new MySqlInitializer());
}
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Some comments online suggest not needing a MySqlInitializer. I hardcoded the db name so simply "get it working".
public class MySqlInitializer : IDatabaseInitializer<ApplicationDbContext>
{
public void InitializeDatabase(ApplicationDbContext context)
{
if (!context.Database.Exists())
{
context.Database.Create();
}
else
{
var migrationHistoryTableExists = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery<int>(
string.Format("SELECT COUNT(*) FROM [hardcodeddbname].__MigrationHistory"));
if (migrationHistoryTableExists.FirstOrDefault() == 0)
{
context.Database.Delete();
context.Database.Create();
}
}
}
}
public class MySqlConfiguration : DbConfiguration
{
public MySqlConfiguration()
{
SetHistoryContext(
"MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));
}
}
public class MySqlHistoryContext : HistoryContext
{
public MySqlHistoryContext(
DbConnection existingConnection,
string defaultSchema)
: base(existingConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();
}
}
Related
I connected the identity model of my WebApp to my MySQL Database, and modified it like I want to connect.
My question is the next: If I want to get data from tables not used in the Identity but still from the same database, What is the correct way to do it? Should I open a new connection and get the data with a class? Is there a way to implement it in the model?
I attach the code to connect to the database:
public void Configuration(IAppBuilder app)
{
DbConfiguration.SetConfiguration(new MySqlEFConfiguration());
ConfigureAuth(app);
context = new ApplicationDbContext();
}
And my connectionStrings:
<connectionStrings>
<add name="DefaultConnection" connectionString="server=x.x.x.x;uid=user;password=pwd;database=db;port=3306;charset=utf8" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
(Sorry if the question is too obvious, I'm starting to develop apps in ASP.NET MVC)
Yes, all you need to do is open the connection when it is required.
So, i assume you did something like this with your MySQL Database connections
public class MySqlInitializer : IDatabaseInitializer<ApplicationDbContext>
{
public void InitializeDatabase(ApplicationDbContext context)
{
if (!context.Database.Exists())
{
// if database did not exist before - create it
context.Database.Create();
}
else
{
// query to check if MigrationHistory table is present in the database
var migrationHistoryTableExists = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery<int>(
string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '{0}' AND table_name = '__MigrationHistory'",
"[Insert your database schema here - such as 'users']"));
// if MigrationHistory table is not there (which is the case first time we run) - create it
if (migrationHistoryTableExists.FirstOrDefault() == 0)
{
context.Database.Delete();
context.Database.Create();
}
}
}}
and your ApplicationDbContext class will be like this
public class ApplicationUser : IdentityUser{}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
static ApplicationDbContext()
{
Database.SetInitializer(new MySqlInitializer());
}
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
Now, to do some queries from database, you need to create an object of ApplicationDbContext
So, your Next Question "Is there a way to implement it in the model?" Yes there is you need to Create a View Model.
public class ItemProduct
{
public int Id {get; set;};
public string Name {get; set;};
}
So, lets say a Product Table exists in your database and we can query on it.
public class HomeController : Controller
{
ApplicationDbContext context = new ApplicationDbContext();
public ActionResult AnyName()
{
List<ItemProduct> lstProducts = new List<ItemProduct>();
var getProducts = (from p in context.Products
select p).ToList();
foreach(var item in getProducts)
{
ItemProduct pro = new ItemProduct()
{
pro.Id = item.Id,
pro.Name = item.Name
};
lstProducts.Add(pro);
}
// So, you have all your Products in ItemProduct Class list.
// Now, return lstProducts to your View to show it on front end
}
}
Hope this answer your question :)
Hi I start learn Fluent NHibernate. I am using this tutorial http://www.d80.co.uk/post/2011/02/20/Linq-to-NHibernate-Tutorial.aspx.
Here is my sample code:
public class Account
{
public virtual int Id { get; set; }
public virtual string Nick { get; set; }
public virtual string Password { get; set; }
}
public class AccountMap:ClassMap<Account>
{
public AccountMap()
{
Id(x => x.Id);
Map(x => x.Nick);
Map(x => x.Password);
}
}
public class NHiberanteHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
InitializeSessionFactory();
return _sessionFactory;
}
}
private static void InitializeSessionFactory()
{
_sessionFactory = Fluently.Configure()
//NHibernate bude pouzivat ovladace pre MS SQL 2008
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(
#"Server=JAN-MSI\SQLEXPRESS;Database=SimpleNHibernate;Trusted_Connection=True;").ShowSql()
)
//urci NHibernatu kde ma hladat mapovacie subory
.Mappings(m=>m.FluentMappings.AddFromAssemblyOf<Account>())
//ak tabs nie su v DB vytvori
.ExposeConfiguration(cfg => new SchemaExport(cfg).Create(true, true))
//vytvori jeden session pre cely life-time apps
.BuildSessionFactory();
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
class Program
{
static void Main(string[] args)
{
using (var session = NHiberanteHelper.OpenSession())
{
using (var trans = session.BeginTransaction())
{
var account = new Account
{
Nick = "dfdwf",
Password = "xxx"
};
session.Save(account);
trans.Commit();
}
}
Console.ReadKey();
}
}
Problem is that this Fluent configuration always drop table in database.
I need only check if table doesnt exist so create table not always when code run drop table.
Your call to new SchemaExport(cfg).Create(true, true) is exporting the configuration to the database. This will drop and re-create it (it's not clever enough to work out the differences and just execute them.
You could use SchemaUpdate, which will update the schema instead. Here's a blog post about it: http://geekswithblogs.net/dotnetnomad/archive/2010/02/22/138094.aspx
I would always prefer to update tables myself or use something like Redgate's SQLCompare to update a schema whilst preserving data.
I'm using EF4.1 with MVC3 and I need an override to prevent EF from creating a db if it doesn't exist. Instead of creating a new db I would like to catch the error and report that the initial catalog (the database name) is invalid in the connect string.
However, during development I would like to allow for updates for new classes/properties to create according tables/cols in the database.
Is there a best practice or pattern here?
In my application i am completly disable context initializer and handle database mapping and schema manually.
For example :
public class AppDbContext : DbContext
{
public IDbSet<Account> Accounts { get; set; }
public AppDbContext() : base("connection_string")
{
Database.SetInitializer<AppDbContext>(null); // Important! Dont use entity framework initializer !important
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
/* Register custom mapping class */
modelBuilder.Configurations.Add(new AccountMapper());
base.OnModelCreating(modelBuilder);
}
}
And custom mapping :
public class AccountMapper : EntityTypeConfiguration<Account>
{
/// <summary>
/// Employee entity mapper
/// </summary>
public AccountMapper()
{
ToTable("accounts");
HasKey(x => x.Id);
...
}
}
I would suggest looking into the EF database initializer, specifically the IDatabaseInitializer interface.
If you just want it to stop creating the database when it doesn't exist, then just set the Initializer to null. But if you want to log the event or something along those lines then simply create your own IDatabaseInitializer - it's not hard.
You can then set the initializer Application_Start in your global.asax.cs like so:
Database.SetInitializer(new YourCustomInitializer());
As a bonus, here's an example IDatabaseInitializer that I use to run database migrations (using FluentMigrator)... it's extremely handy if I do say so myself!
public class MigrationsDbContextInitializer : IDatabaseInitializer<YourDbContext>
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MigrationsDbContextInitializer));
public void InitializeDatabase(YourDbContext context)
{
var announcer = new BaseAnnouncer(x => Logger.Info(x));
var runnerContext = new RunnerContext(announcer)
{
Database = "sqlserver2008",
Connection = context.Database.Connection.ConnectionString,
Target = "YourEntitiesNamespace",
PreviewOnly = false,
Task = "migrate"
};
new TaskExecutor(runnerContext).Execute();
}
}
public interface IRepository<T>// : IDisposable
where T : IEntity
{
IQueryable<T> GetAll();
void Save(T entity);
void Delete(int id);
void Delete(T entity);
}
public abstract class RepositoryBase<T, TDb> : IRepository<T>
where T : IEntity
where TDb : class, IDbEntity, new()
{
protected abstract Table<TDb> GetTable();
public void Delete(int id)
{
TDb dbEntity = GetDbEntity(id);
if (dbEntity == null)
{
throw new MyException(...);
}
GetTable().DeleteOnSubmit(dbEntity);
Context.SubmitChanges();
}
// and other methods...
}
For now it is necessary to implement method that deletes entities by expression. I would like to have the following method in IRepository:
void DeleteWhere(Expression<Func<IEntity, bool>> exprFoeBusinessEntity);
The problem here is that implementation would look like this:
void DeleteWhere(Expression<Func<IEntity, bool>> exprFoeBusinessEntity);
{
Expression<Func<IDbEntity, bool>> exprFoeDbEntity
= exprWhere=> ... // How to convert exprFoeBusinessEntity into exprFoeDbEntity
GetTable().DeleteAllOnSubmit(GetTable().Where(exprForDbEntity)));
}
And I don't know how to convert expression for business entities into expression for db-entities...
I could easily change method to accept expression with db-entities, but I would like my Repository to hide DBEntities inside.
Please advise. Any thoughts are welcome.
P.S. I am working with .NET 3.5 (but solutions for 4.0 are also acceptable), ORM - Linq2Sql
Guess, I've found a good solution, here is a new method for RepositoryBase class:
public void Delete(IQueryable<T> entities)
{
IQueryable<TDb> dbEntities = GetTable()
.Where(
dbEntity => entities.Where(entity => entity.Id == dbEntity.Id).Count() > 0
)
;
GetTable().DeleteAllOnSubmit(dbEntities);
}
Please point me if you see any drawbacks here.
Was I was previously messing with Silverlight and RIA, I was using a an ADO.NET Entity Data Model and domain context. I'm teaching myself how to run a stored procedure from RIA, and could not get it to work with the previous setup so I then started to try out using the LINQ to SQL method. I did the following:
Added the DBML
Drag and dropped my table and example stored proc
Saved the DBML and built and rebuilt the solution and project files.
Create a Domain Service and attempt to add the dbml.
Here is my DBML
Here is my error. The Available DataContext/ObjectContext classes list shows up empty. I have rebuilt many times and even have restarted visual studio.
So here are my questions:
Is there an easy way to do a stored proc with ADO.Net that I was missing therfore I could skip adding a LINQtoSQL class.
Am I missing something really simple to add the dbml class and add a stored proc that way.
CODE
ZipCodeDataClasses.dbml.layout
<?xml version="1.0" encoding="utf-8"?>
<ordesignerObjectsDiagram dslVersion="1.0.0.0" absoluteBounds="0, 0, 11, 8.5" name="ZipCodeDataClasses">
<DataContextMoniker Name="/ZipCodeDataClassesDataContext" />
<nestedChildShapes>
<classShape Id="de7acfae-754b-4577-b227-3a33198fdc95" absoluteBounds="7.125, 2.375, 2, 1.3862939453125">
<DataClassMoniker Name="/ZipCodeDataClassesDataContext/ZipCode" />
<nestedChildShapes>
<elementListCompartment Id="8744ad5e-7b77-4770-911d-6dc963876e40" absoluteBounds="7.14, 2.835, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
</nestedChildShapes>
</classShape>
</nestedChildShapes>
</ordesignerObjectsDiagram>
ZipCodeDataClasses.designer.cs
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.1
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace StoredProcedureTest.Web
{
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
using System.ComponentModel;
using System;
[global::System.Data.Linq.Mapping.DatabaseAttribute(Name="ZIPCODES")]
public partial class ZipCodeDataClassesDataContext : System.Data.Linq.DataContext
{
private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
public ZipCodeDataClassesDataContext() :
base(global::System.Configuration.ConfigurationManager.ConnectionStrings["ZIPCODESConnectionString"].ConnectionString, mappingSource)
{
OnCreated();
}
public ZipCodeDataClassesDataContext(string connection) :
base(connection, mappingSource)
{
OnCreated();
}
public ZipCodeDataClassesDataContext(System.Data.IDbConnection connection) :
base(connection, mappingSource)
{
OnCreated();
}
public ZipCodeDataClassesDataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
base(connection, mappingSource)
{
OnCreated();
}
public ZipCodeDataClassesDataContext(System.Data.IDbConnection connection, System.Data.Linq.Mapping.MappingSource mappingSource) :
base(connection, mappingSource)
{
OnCreated();
}
public System.Data.Linq.Table<ZipCode> ZipCodes
{
get
{
return this.GetTable<ZipCode>();
}
}
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.sp_GetCityByZip")]
public int sp_GetCityByZip([global::System.Data.Linq.Mapping.ParameterAttribute(Name="ZIP", DbType="NVarChar(10)")] string zIP, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="Return", DbType="NVarChar(26)")] ref string #return)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), zIP, #return);
#return = ((string)(result.GetParameterValue(1)));
return ((int)(result.ReturnValue));
}
}
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.ZipCodes")]
public partial class ZipCode
{
private string _City;
private string _State;
private int _Zip;
public ZipCode()
{
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_City", DbType="VarChar(25)")]
public string City
{
get
{
return this._City;
}
set
{
if ((this._City != value))
{
this._City = value;
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_State", DbType="VarChar(2)")]
public string State
{
get
{
return this._State;
}
set
{
if ((this._State != value))
{
this._State = value;
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Zip", DbType="Int NOT NULL")]
public int Zip
{
get
{
return this._Zip;
}
set
{
if ((this._Zip != value))
{
this._Zip = value;
}
}
}
}
}
#pragma warning restore 1591
I gave up trying to get Stored Procedures to work with Silerlight, however following this guide was the closest I ever came to an actual implementation. I hope it helps.
http://anhonga.wordpress.com/2010/02/17/using-stored-procedures-with-silverlight-4riapocos-part-ii/
If you ever find a simple way to get stored procedures implemented let me know, because I am in the middle of rewriting a few hundred procedures in LINQ.