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
Related
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.
so I have 2 different DbContext (ef 6.1.3 code first)
FirstDbContext
SecondDbContext
each context contains a SbSet Users that maps the user table in the corresponding database
NOTE : the data is different, DbFirst User is not DbSecond User!!
I have an abstract repository:
public abstract class Repository<TContext> where TContext : DbContext
{
public Repository(TContext ctx)
{
}
}
and 2 repositories :
public FirstRepo : Repository<FirstDbContext>
{
public FirstRepo(FirstDbContext ctx):base(ctx)
{
}
}
public SecondRepo : Repository<SecondDbContext>
{
public SecondRepo(SecondDbContext ctx):base(ctx)
{
}
}
I Have 2 different MSSQL databases related to the contexes:
DbFirst
DbSecond
I'm using dependency injection to add scoped repository and contexes, 2 database, 2 different connection.
I expected that my .Net Core application would use 2 Models
but once i get data from both the context i get
NotSupportedException: The type 'First.User' and the type
'Second.User' both have the same simple name of
'User' and so cannot be used in the same model.
Why the same model?
I know that in the same model I should have different names because EF does not look for namespaces, but in that case I shouldn't have this kind of issue.
EDIT #1 :
If I use one of the repository alone everything works as expected so i'm sure that there isn't any mispelled namespace
If I use the repositories all together i got this error, for example
var top10 = FirstRepo.GetTop10().ToList();
var sam = SecondRepo.GetByName<Second.User>("sam");
EDIT 2 (#Steve Green):
//Note that I'm not trying to do this :
public class MyTextContext : DbContext
{
public DbSet<Test.Security.Question> Security_Question { get; set; }
public DbSet<Test.Forms.Question> Forms_Question { get; set; }
}
// What I need is something like this :
public class SecurityContext : DbContext
{
public DbSet<Test.Security.Question> Question { get; set; }
}
public class FormsContext : DbContext
{
public DbSet<Test.Forms.Question> Question { get; set; }
}
Important note
If I manually ignore the "other" entity in both of the context everything works
I Remark that the context are not only in different namespaces, but also different assemblies...
// this is working!! .___.
// but i don't want to add a reference to a project just to exclude a class... it's unacceptable
public class FirstDbContext : DbContext
{
public DbSet<First.User> Users {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Second.User>();
}
}
public class SecondDbContext : DbContext
{
public DbSet<Second.User> Users {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<First.Usere>();
}
}
Any suggestion different from renaming the table will be appreciated
Thanks
I'm running into an InvalidOperationException because "An entity object cannot be referenced by multiple instances of IEntityChangeTracker." on the first line of EntityFrameWorkRepository.Create().
I know this is due to having multiple database contexts, but in this case I'm a bit lost as the code has no obvious second context since all database access goes through a designated object whose sole purpose is managing database contexts. This was done as the web application in question is fairly interactive and so the user is constantly creating new objects which must be saved in the database. This was causing issues with the previous design, which used locking and a single context, thus the code was refactored and works, except for the method in question.
EF class:
public class EntityFrameWorkRepository<TKey, TEntity> : IDisposable, IRepository<TKey,TEntity> where TEntity: class
{
private readonly IDbContext _context;
private IDbSet<TEntity> _entities;
public EntityFrameWorkRepository()
{
_context = new ApplicationDbContext();
}
private IDbSet<TEntity> Entities
{
get { return _entities ?? (_entities = _context.Set<TEntity>()); }
}
public void Create(TEntity entity)
{
Entities.Add(entity);
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
The service object used for all DB access:
public class Service : IService
{
public const string Persistance = "Persist";
public const int CacheTaskSeconds = 300; //Check every 5 minutes
public const double IdleMinutes = 30.0;
private readonly IKvpRepository<int, SimulationCollection> _simulationCollectionAppStateRepository;
private readonly UserManager<ApplicationUser> _userManager;
public Service(IKvpRepository<int, SimulationCollection> simulationCollectionAppStateRepository)
{
_userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
AddTaskToCache(Persistance, CacheTaskSeconds);
}
public SimulationCollection CreateCollection(Guid userId, string name, string description)
{
using (var _simulationCollectionEFRepository = new EntityFrameWorkRepository<int, SimulationCollectionEntity>())
{
var applicationUser = _userManager.FindById(userId.ToString());
if (applicationUser == null)
throw new ArgumentOutOfRangeException("ApplicationUser matching userId doesn't exist");
var collectionEntity = new SimulationCollectionEntity(applicationUser, name, description);
_simulationCollectionEFRepository.Create(collectionEntity);
return collection;
}
}
}
The object I'm trying to add to the database:
public class SimulationCollectionEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
public string Name { get; set; }
public virtual ApplicationUser User { get; set; }
public DateTime DateCreated { get; set; }
public string Description { get; set; }
[ForeignKey("SimulationCollectionEntityId")]
public virtual ICollection<SimulationEntity> Simulations { get; set; }
[Obsolete("Only needed for serialization and materialization", true)]
public SimulationCollectionEntity() {}
public SimulationCollectionEntity(ApplicationUser currentUser, string name, string description)
{
User = currentUser;
Name = name;
Description = description;
DateCreated = DateTime.Now;
}
}
Is there an easy way to view what contexts a given object might be attached to? I already checked to see if collectionEntity is attached to _userManager since it has a dbContext, but its state is detached. Does EF maybe expect me to add objects in a different way than I am? I suspect that the attributes in SimulationCollectionEntity might be causing me trouble but I'm new to Entity Framework and I'm not sure. Should I maybe be going for a different design instead like this?
You might want to consider a unit of work like approach where one context is shared among multiple repositories. The accepted answer for this post is a good example. I have seen ContextPerRequest solutions like the one in your example, but I've never been crazy about them. Ideally you want a short lived context that does one thing like add an invoice and two invoice items - a single unit of work. You could then wrap the whole operation in a TransactionScope and have it succeed or fail as a unit.
Is it possible (with attributes or something) to configure DAO methods to return detached objects? I am keen to do this, because I want to make sure that the DAO pre-fetches any fields and relationships that might be required by downstream code on return from the DAO. If the entities are detached, then an exception will be thrown and we can identity the issue easily. With lazy resolution of relationships, you potentially get multiple additional requests to the DB without realising it.
For example, let's say I have a DAO class:
public class TestDao
{
private readonly MyContext _db;
public TestDao(MyContext db)
{
_db = db;
}
public List<Group> AllGroups()
{
return _db.Groups.ToList();
}
}
And then say that I have a client of the Dao:
public void TestGetAllGroups()
{
var groups = _testDao.AllGroups();
foreach (var group in groups)
{
var x = group.Memberships;
Console.WriteLine( group.id + ":" + x.Count );
}
}
This code works, but each iteration in the test harness causes a new hit to the DB because the DB hasn't pre-fetched (included) the Memberships relationship.
I'm looking for the best way to get this code to throw an exception, saying that group.Memberships is null or something. If the Group instances were detached upon exit from TestDao.AllGroups(), then this would do the trick, and alert us to the fact that the DAO needs to include the Memberships before returning from the AllGroups() method
Looks like you can disable Lazy Loading of relationships. In my Context class:
public partial class MyContext : DbContext
{
static MyContext()
{
Database.SetInitializer<MyContext>(null);
}
public MyContext() : this("Name=MyContext")
{
}
public MyContext(string name): base(name)
{
Configuration.LazyLoadingEnabled = false;
}
...
}
However, in addition to this, I need to make sure that the EF entity constructor doesn't create empty lists for the one-to-many relations. These seem to get added by default with the schema-first entity code generation tool:
public partial class Group
{
public Group()
{
//Remove this line!
//this.Memberships = new List<Membership>();
}
public int id { get; set; }
public string name { get; set; }
public virtual ICollection<Membership> Memberships { get; set; }
}
Now, my test harness will throw a NPE unless I put a .Include into the Dao to include Memberships
I'm working on a database factory pattern for an application which should support Sql Server and Oracle. I've an abstract classes with all the Sql Queries for the application. I've implemented the abstract class in two classes: SqlServerClass and OracleClass. Based on the connection string defined in the configuration file, the application creates an instance of the corresponding class and get the Sql queries for the database.
public abstract class ProviderFactory
{
public abstract string GetCustomersSql();
public abstract string GetCustomersByIdSql();
public abstract string GetUsersSql();
public abstract string GetUsersByIdSql();
}
public class OracleClass : ProviderFactory
{
public override string GetCustomersSql()
{
// return sql query for Oracle
}
// other methods
}
public class SqlServerClass : ProviderFactory
{
public override string GetCustomersSql()
{
// return sql query for Sql Server
}
// other methods
}
Now my question is, Is there a way to group these sql queries in the abstract class so that one can easily identify the sql queries used for particular functionality. For example, Can I group all Customers related queries and Users related queries so that when I refer them, it would be like....
ProviderFactory instance;
// create an instance
instance.Customers.GetCustomersSql();
Is what I'm doing here a valid approach? Please suggest. Thank you.
I would strongly recommend using an ORM such as NHibernate. It supports both SQL Server and Oracle and abstracts away the differences between the two.
By that I mean you only have to write the query once in a format NHibernate understands and it will translate that to versions that SQL Server and Oracle understand.
If you want to continue down your current path what you can do is create what I'd call a query directory:
public interface IQueryDirectory
{
ICustomerQueries Customer { get; }
IUserQueries User { get; }
}
public interface ICustomerQueries
{
string Customers { get; }
string CustomersById { get; }
}
public interface IUserQueries
{
string Users { get; }
string UsersById { get; }
}
Example implementation:
public abstract class QueryDirectory : IQueryDirectory
{
private ICustomerQueries customer;
private IUserQueries user;
public ICustomerQueries Customer
{
get { return customer; }
}
public IUserQueries User
{
get { return user; }
}
protected QueryDirectory(ICustomerQueries customer, IUserQueries user)
{
this.customer = customer;
this.user = user;
}
}
public class SqlServerQueryDirectory : QueryDirectory
{
public SqlServerQueryDirectory(SqlServerCustomerQueries customer,
SqlServerUserQueries user) : base(customer, user) {}
}
public class SqlServerCustomerQueries : ICustomerQueries
{
public string Customers
{
get "some sql";
}
public string CustomersById
{
get "some sql";
}
}
Then you can implement the directories seperately for each database. But honestly, I really, really, really recommend using an ORM instead.