EF first database change connection string - c#

Hi I have server with some databases that have the same schema. I use EF6 Database/Model First code and I do not want to create deterrent DbContext for them. for example my generated DbContext is :
public partial class TEST_Rev5_FINALEntities : DbContext
{
public TEST_Rev5_FINALEntities()
: base("name=TEST_Rev5_FINALEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Header> tbl_Headers { get; set; }
public virtual DbSet<Output> tbl_Output { get; set; }
public virtual DbSet<Run> tbl_Run { get; set; }
}
and I created a partial class to set the connection string
public partial class TEST_Rev5_FINALEntities : DbContext
{
public TEST_Rev5_FINALEntities(DbConnection dbConnection)
: base(dbConnection, true)
{
}
}
And I have the following method to create the connection with deterrent connection string:
public DbConnection GetConnectionString()
{
DbConnection conn;
SqlConnectionStringBuilder sqlConnectionStringBuilder = new SqlConnectionStringBuilder
{
DataSource = DataSource,
IntegratedSecurity = false,
UserID = User,
Password = Password,
MultipleActiveResultSets = true
};
SqlConnectionFactory sqlConnectionFactory = new SqlConnectionFactory(sqlConnectionStringBuilder.ConnectionString);
conn = sqlConnectionFactory.CreateConnection(DatabaseName);
return conn;
}
Finally I try to run it like this:
using (var context = new TEST_Rev5_FINALEntities(_dal.Connector.GetConnectionString()))
{
return context.tbl_Headers.FirstOrDefault();
}
but I get this error :
System.Data.Entity.Infrastructure.UnintentionalCodeFirstException
HResult=0x80131509 Message=The context is being used in Code First
mode with code that was generated from an EDMX file for either
Database First or Model First development.
How can I do it?

The behavior EF uses depends on the way your connection string looks. If it includes a metadata attribute like this:
metadata=res://*/model.csdl|res://*/model.ssdl|res://*/model.msl;
It will presume you are using Database or Model first development.
To make sure Code First is used, remove metadata part of the connection string.

Related

How do I create objects based on a query in EF Core (keyless entities)?

I am trying to map the results of a complex SQL query to a keyless entity in EF Core. (I basically need to create a view client-side due to having to deal with a preexisting database.)
However, when trying to retrieve my data, I get an InvalidOperationException "Sequence contains no elements". I can map base tables in the DB to normal entities, so I know the connection string is correct. A simplified, but complete example:
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
class Program
{
static void Main(string[] args)
{
using (var db = new QueryDbContext())
{
List<Model> result = db.Orders.ToList(); // <-- exception occurs here.
Console.WriteLine($"Found: {result.Count}");
}
}
}
public class Model
{
public string OrderId { get; }
}
public class QueryDbContext : DbContext
{
public QueryDbContext()
: base()
{}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
_ = optionsBuilder
.UseSqlServer("myconnectionstring");
}
}
public DbSet<Model> Orders { get; set; }
private const string _OrdersSql =
"select top 5 orderid from tbl_orders order by transacttime desc";
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Model>(m =>
{
m.HasNoKey();
m.ToQuery(() => Set<Model>().FromSqlRaw(_OrdersSql));
});
}
}
Set<>() is the current version (since EFCore 3.0) of Query<>() as described in the answer to FromSql with Non-Existing Entity.
The problem was that the model properties need both setters and getters, even though the model is read-only. So the model should be
public class Model
{
public string OrderId { get; set; }
}

How to check if SQLite Database exists and then create it

I have data in a SQLite Database. But I can not be sure that it will always be there. So when I start my Program I first want to check if the SQLite Database exists and when not I want to create one with the DbSet's I already have in my DbContext.
public class MaintenanceDB : DbContext
{
public MaintenanceDB() : base (new SQLiteConnection(new
SQLiteConnectionStringBuilder { DataSource = "data.sqlite"}.ConnectionString), true)
{
}
public DbSet<MaintenanceEntry> MaintenanceEntries { get; set; }
public DbSet<ModuleEntry> ModuleEntries { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MaintenanceEntry>().ToTable("some Table");
modelBuilder.Entity<ModuleEntry>().ToTable("some Table");
}
}
When I delete my SQLite Database and startup my Program again then I want my SQLite Database to be created.
<connectionStrings>
<add name="MaintenanceDB" connectionString="" providerName="System.Data.SqLite" />
</connectionStrings>
public class MaintenanceDB : DbContext
{
public MaintenanceDB() : base ("Name=MaintenanceDB")
And try the solutions below:
var context = new MaintenanceDB();
if (!context.Database.Exists())
context.Database.Create();
Or
var context = new MaintenanceDB();
context.Database.CreateIfNotExists();
Or create an initializer class as below:
// public class ContentInitializer: DropCreateDatabaseAlways <MaintenanceDB>
// public class ContentInitializer: CreateDatabaseIfNotExists <MaintenanceDB>
public class ContentInitializer: DropCreateDatabaseIfModelChanges <MaintenanceDB>
And put this at the beginning of the application.
Database.SetInitializer (new ContentInitializer ());

Mysql custom connection string

I have entity framework 5 with the mysql connector installed (mysql for vs 1.1.3 and mysql connector 6.8.3) and have a connection string in my app.config:
<add name="applicantDBEntities" connectionString="metadata=res://*/Models.applicantsDBModel.csdl|res://*/Models.applicantsDBModel.ssdl|res://*/Models.applicantsDBModel.msl;provider=MySql.Data.MySqlClient;provider connection string="server=localhost;user id=root;database=applicantsdb"" providerName="System.Data.EntityClient" />
If I use the connection string in the app.config, everything works, but I want the user to be able to choose his server, username, password and DB. How i tried to implement it:
public partial class applicantDBEntities : DbContext
{
public applicantDBEntities()
: base("name=applicantDBEntities")
{
}
public applicantDBEntities(string connectionString) : base (connectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<applicant> applicants { get; set; }
public DbSet<file> files { get; set; }
}
And the initialization itself:
CONNECTION_PATTERN = "server={0};database={1};User Id={2};password={3}";
this.dbContext = new applicantDBEntities(String.Format(CONNECTION_PATTERN,
Settings.GetInstance().Server, Settings.GetInstance().Database,
Settings.GetInstance().User, Settings.GetInstance().Password));
but no matter what I pass as arguments (even wrong ones) I always end up in the OnModelCreating method.
What is the correct way to do this?
EDIT:
As #Andrew mentioned, my connection string was wrong. This is how I do it now:
string connectionString = new System.Data.EntityClient.EntityConnectionStringBuilder
{
Metadata = "res://*",
Provider = "MySql.Data.MySqlClient",
ProviderConnectionString = new System.Data.SqlClient.SqlConnectionStringBuilder
{
InitialCatalog = Settings.GetInstance().Database,
DataSource = Settings.GetInstance().Server,
IntegratedSecurity = false,
UserID = Settings.GetInstance().User,
Password = Settings.GetInstance().Password
}.ConnectionString
}.ConnectionString;
this.dbContext = new applicantDBEntities(connectionString);
Your connection string is in invalid format.
It should resemble this:
"metadata=res://*/Northwind.csdl|
res://*/Northwind.ssdl|
res://*/Northwind.msl;
provider=System.Data.SqlClient;
provider connection string=
"Data Source=.\sqlexpress;
Initial Catalog=Northwind;
Integrated Security=True;
MultipleActiveResultSets=True""
and might be based on EntityConnectionStringBuilder
http://msdn.microsoft.com/en-us/data/jj592674.aspx here's description of how you should connect to database for different scenarios and framework versions

Using Base Entity Context for code first Entity Framework

TLDR: Why is Entity Framework appending extra path to the end of my datasource? What do I need to do in order to use this framework? Is there another way to handle this?
I am writing a basic SQL Server CE winforms program to store some data. I originally had 1 context to be used to interact with 1 class. Now, I wish to add another class, and so I refactored out a base context that the original context could be derived from. I have the code running, although I can't create any database with it.
I take a path in from the user to the database and set the |DataDirectory|, which I use for the data source.
<add name="MonsterContext"
connectionString="Data Source=|DataDirectory|; Persist Security Info=False"
providerName="System.Data.SqlServerCe.4.0" />
I intend for the data source to look like
C:Path\To\DatabaseFile.sdf
but it comes out as
C:\Path\To\DatabaseFile.sdf\Namespace.BaseContext`1[Namespace.ModelClass].sdf ]"}
I think this path demonstrates that it is building its own source based on EF defaults.
My base Context is:
public class EntityContext<T> : DbContext where T : class
{
public string TableName { get; set; }
public EntityContext()
: base("name=MonsterContext")
{
}
public EntityContext(string tableName)
{
this.TableName = tableName;
}
public DbSet<T> Entities { get; set; }
}
And My Derived Context is:
public class MonsterReader : Reader<Monster>
{
private List<Monster> monsters;
public List<Monster> Monsters
{
get
{
if (monsters == null)
{
monsters = ReadAll();
}
return monsters;
}
private set { monsters = value; }
}
public MonsterReader(string file)
: base(file)
{ }
public MonsterReader(Stream reader)
: base(reader)
{ }
public void CreateMonsterDatabase(IEnumerable<Monster> monsters)
{
CreateDatabase(monsters, TableName);
}
private string TableName { get; set; }
}
In this problem, it was a simple omission from the base context.
In the Base Entity Context, the first constructor is:
public EntityContext()
: base("name=MonsterContext")
{
}
However, the constructor that takes a table name is
public EntityContext(string tableName)
{
this.TableName = tableName;
}
which is missing the : base("name=MonsterContext")
This call is necessary if you want to call a specific connection string from the app.config.
In this case, the connection string you want to call is "Monster Context". Without specifying which context, Entity Framework defaults using SQL Server CE to
|DataDirectory|\Namespace.NameofContext.sdf

Exception of type 'System.ObjectDisposedException'

I have following EF code first code. It is working fine. But when I look for the value of 'club2.Members', it says following:
'club2.Members' threw an exception of type 'System.ObjectDisposedException'.
Message is:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I have not set value for Members property. That’s okay. However it should not cause any exception internally.
Is there a way to overcome this exception?
Does removal of this exception help in improving performance?
Why this exception did not cause termination of program execution?
Note: I don't need the Members data in club entity (at the point where I have called). Even there is no members added to the club at that time. All i need is to get rid of the exception; not to load data using eager load. How to avoid the exception?
namespace LijosEF
{
public class Person
{
public int PersonId { get; set; }
public string PersonName { get; set; }
public virtual ICollection<Club> Clubs { get; set; }
}
public class Club
{
public int ClubId { get; set; }
public string ClubName { get; set; }
public virtual ICollection<Person> Members { get; set; }
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
//Fluent API - Plural Removal
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Person> Persons { get; set; }
public DbSet<Club> Clubs { get; set; }
}
}
static void Main(string[] args)
{
Database.SetInitializer<NerdDinners>(new MyInitializer());
CreateClubs();
CreatePersons();
}
public static void CreateClubs()
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
Club club1 = new Club();
club1.ClubName = "club1";
Club club2 = new Club();
club2.ClubName = "club2";
Club club3 = new Club();
club3.ClubName = "club3";
db.Clubs.Add(club1);
db.Clubs.Add(club2);
db.Clubs.Add(club3);
int recordsAffected = db.SaveChanges();
}
}
public static Club GetClubs(string clubName)
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
var query = db.Clubs.SingleOrDefault(p => p.ClubName == clubName);
return query;
}
}
public static void CreatePersons()
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
Club club1 = GetClubs("club1");
Club club2 = GetClubs("club2");
Club club3 = GetClubs("club3");
//More code to be added (using db) to add person to context and save
}
}
You're getting this error because you are disposing the dbcontext after you execute the query. If you want to use the data in "members" then load it with the query:
http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
If you don't need lazy loading, which means that only data you're loading in the query is loaded (this will eliminate the exception), set LazyLoadingEnabled = false. Look here:
Disable lazy loading by default in Entity Framework 4
APPROACH 1
There is no need for the following method. This can be done in the present context. It is only a select.
GetClubs("club1");
APPROACH 2
Attaching the entities resolved the problem:
Club club1 = GetClubs("club1");
Club club2 = GetClubs("club2");
Club club3 = GetClubs("club3");
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)club1);
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)club2);
((IObjectContextAdapter)db).ObjectContext.Attach((IEntityWithKey)club3);
var test = club2.Members;
I should not have tried to refer club2.Members (of Club class), since it is not needed for creating Person object.
Members is related data in Club class. Since it is virtual, it will do lazy loading.
In a different scenario, if I had to retrieve Members object (which is a related data) from a different context, I should rely on eager loading (in the place which provides you with Club object).
Refer following also:
Entity Framework: Duplicate Records in Many-to-Many relationship
System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
ObjectContext instance has been disposed while binding

Categories

Resources