Entity Framework 6.0 with empty DbContext-Constructor - c#

I have one ASP-WebForms-Application for 3 Companies. Every Company has its own Database and own EDMX-Model. The Structure in the Databases is the same. On Parameters I am checking which Company it is and want to have one DbContext for all the Models.
I am very new in Entity Framework and don't know how to make one Context for a few Models.
I have tried to make a class that gives me a DbContext, in which I want to make the DBContext one of the Models.
I am trying:
public static DbContext holeDbContextWsvWsbSvs()
{
DbContext context = new DbContext();
string verbandKürzel = config.holeVerbandKürzel();
if (verbandKürzel == "wsv")
{
context = new wsvEntities();
}
else if (verbandKürzel == "wsb")
{
context = new wsbEntities();
}
else if (verbandKürzel == "svs")
{
context = new svsEntities();
}
return context;
}
But in Entity Framework 6.0 it seems as an emtpy Constructor is not possible!
Is it possible to intialize a Null(Fake,Pseudo)-DbContext or something and then change it to the Model-Context?
Can anyone give me an impulse please, how can i realize my plans?
EDIT:
Okay, changing the Context to the Model inside the Method achieved by giving the Constructor any string. But although giving the Context a specific Model inside the method, I am returning a DbContext an can not use Object-Attributes after that.
Any Suggestions? Or do really have to make at every position i need to work with the DataModel IF-ELSEs and do the same logic for each Model?

If the structure of the DbContexts are identical and you just want to point at one of three databases, you don't need to declare 3 separate DbContexts, just one, then use the Constructor argument to nominate the appropriate connection string.
public class AppDbContext : DbContext
{
// Define DbSets etc.
public AppDbContext(string connection)
: base (connection)
{ }
}
Then in your initialization code:
public AppDbContext holeDbContextWsvWsbSvs()
{
string verbandKürzel = config.holeVerbandKürzel();
switch (verbandKürzel)
{
case "wsv":
return new AppDbContext("wsvEntities");
case "wsb":
return new AppDbContext("wsbEntities");
case "svs":
return new AppDbContext("svsEntities");
default:
throw new InvalidOperationException("The configured client is not supported.");
}
}
Assuming you have connections strings called "wsvEntities", "wsbEntities", and "svsEntities" respectively.
If the DbContexts are not identical in structure then honestly you likely will have much bigger problems that won't be solved by exposing them via the base DbContext.

Related

C# How to connect to different databases based on a flag

I'm trying to create a common function in a C# Webapi project to connect to one of two databases based on the value of an input flag. Each database has the same structure, but different data and each invocation would use the same database throughout.
The rest of my code is database agnostic, so it needs to be able to use a common db object, rather than making the decision every time a call to the database is done.
This is the code I thought would do the trick:
public static dynamic GetDb(string scope = "dev") {
dynamic db;
if (Globals.db == null) {
switch (scope.ToLower()) {
case "tns":
db = new TnsDb();
break;
case "sng":
db = new SngDb();
break;
default:
db = new TnsDb();
break;
}
Globals.db = db;
} else {
db = Globals.db;
}
return db;
}
I'm using the Entity Framework, which has created an access class for each database I've connected to the project and contains a method for each stored procedure I need to access. What I would like to do is to get this common method to return an object representing the appropriate database class.
The problem I'm having is that the method isn't returning a usable object. I've tried using a return type of dynamic (as above), a plain object and DbContext (the parent class of the Entity Framework db class) and a few other things out of desperation, but each time the calling statement receives back an object that has none of the methods present in the Entity Framework database class.
Any ideas of how to dynamically select and return the appropriate object that does retain all the methods will be gratefully received, as I will be able to save at least some of my hair.
You mention that each database has the same structure yet your functions uses different db context. You cant use dynamic object in this context using dynamic object.
Since your db has the same structure you could just change the connection string to the db when you initialize the DbContext. If you really want to use separate db contexts then you should use an interfact which your fuctions returns
IMyDatabase
{
BbSet<Model1> Model1{get;set;}
BbSet<Model2> Model2{get;set;}
}
TnsDb:IMyDatabase
{
BbSet<Model1> Model1{get;set;}
BbSet<Model2> Model2{get;set;}
}
SngDb:IMyDatabase
{
BbSet<Model1> Model1{get;set;}
BbSet<Model2> Model2{get;set;}
}
and both of you context need to implement this then your fuctions could be like this
public static IMyDatabase GetDb(string scope = "dev") {
dynamic db;
if (Globals.db == null) {
switch (scope.ToLower()) {
case "tns":
return new TnsDb();
case "sng":
return new SngDb();
default:
return new TnsDb();
}
}
But you shouldn't be using two separate db context just one would be enough in your case with different connection string
Although using static functions for this purpose not really good, you could some repository pattern using dependency injection
Success! I ended up creating an interface that had the same methods as the db class and returned that from the common method.
Here's a snippet from the interface:
public interface IDatabase {
ObjectResult<Method_1_Result> Method_1(Nullable<int> id, string username);
ObjectResult<Method_2_Result> Method_2(string username, string password);
}
and here are the equivalent methods from the db classes:
public partial class TnsDb : DbContext, IDatabase {
public TnsDbDev()
: base("name=TnsDb")
{
}
public virtual ObjectResult<Method_1_Result> Method_1(Nullable<int> id, string username)
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Method_1_Result>("Method_1", idParameter, usernameParameter);
}
public virtual ObjectResult<Method_2_Result> Method_2(string username, string password)
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Method_2_Result>("Method_2", usernameParameter, passwordParameter);
}
}
public partial class SngDb : DbContext, IDatabase {
public SngDbDev()
: base("name=SngDb")
{
}
public virtual ObjectResult<Method_1_Result> Method_1(Nullable<int> id, string username)
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Method_1_Result>("Method_1", idParameter, usernameParameter);
}
public virtual ObjectResult<Method_2_Result> Method_2(string username, string password)
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Method_2_Result>("Method_2", usernameParameter, passwordParameter);
}
}
Note that I had to delete the get and set bodies from the interface method, as they weren't needed and kept generating errors.
I also suspect that this solution is specific to my situation of needing to connect to two databases with exactly the same schema and wouldn't work if the schemas were slightly different, as they couldn't share the same interface.
The technique also requires you to remember to re-add the reference to the interface every time you re-generate the db class (in this case. the TnsDb class) and keep it up to date whenever you change any methods in the db class.
Anyway, I hope that helps anyone with the same problem as I had. Thanks to all that helped me solve this.

Entify Framework: Is it legal to reuse an entity added to a disposed context

Suppose I have created an Entity Framework entity1 and added it to dbContext1.
I then, for any reason, Dispose of dbContext1 and create a fresh DbContext2.
Is it OK to now reuse entity1 by adding it to dbContext2?
EF Version is 6.1.3.
public class DbSaver
{
MyDbContext _context;
public DbSaver()
{
MyDbContext _context = new MyDbEntities();
}
public void MustDoSave(/*whatever*/)
{
bool saved = false;
MyEntity entity1 = new MyEntity() { /*setup entity field values here*/ };
_context.Add(entity1);
while (!saved)
{
try
{
_context.SaveChanges();
saved = true;
}
catch
{
_context.Dispose(); // dispose the original context ("contextA)
_context = new MyDbEntities(); // create a fresh one
// the questionable part:
_context.Add(entity1); // is it legal to now reuse an entity previously added to the old context ("contextB")??
Thread.Sleep(1000); // wait before fresh retry
}
}
}
}
It is a tricky question. If the changes were persisted, then the entity will have identity field populated. Don't assume that it will not be populated just because the transaction has been rolled back - that is the implementation detail of the DbContext class.
Consequently, if you add the same entity to the new DbContext assuming that it is still new, i.e. without its identity field populated, you might get into troubles when you try to persist changes for the second time.
My advice is not to do that, since correctness of your code then depends on implementation details of Entity Framework. Needless to say, implementation may change in the future and then your code which seems to be working fine today will stop working tomorrow.
Yes, it is, as the entity does not keep a reference to the context (or contexts) that it is currently associated with.

The instance of entity type 'SalesOrder' cannot be tracked because another instance of this type with the same key is already being tracked

I am using .net core.
My Goal: I want to be able Edit a SalesOrder just after Creating.
Right now I am able to Create and Edit. But it is throwing an error
The instance of entity type 'SalesOrder' cannot be tracked because
another instance of this type with the same key is already being
tracked. When adding new entities, for most key types a unique
temporary key value will be created if no key is set (i.e. if the key
property is assigned the default value for its type). If you are
explicitly setting key values for new entities, ensure they do not
collide with existing entities or temporary values generated for other
new entities. When attaching existing entities, ensure that only one
entity instance with a given key value is attached to the context.
When I try editing just after creating.
My Save() function:
public class SalesOrdersController : Controller
{
private readonly ApplicationDbContext _dbContext;
public SalesOrdersController(ApplicationDbContext dbContext){
_dbContext = dbContext;
}
// ...other Controller actions
public JsonResult Save([FromBody]SalesOrderViewModel salesOrderViewModel)
{
SalesOrder salesOrder = new SalesOrder();
salesOrder.document_id = salesOrderViewModel.document_id;
salesOrder.customer = salesOrderViewModel.customer;
salesOrder.document_status_id = salesOrderViewModel.document_status_id;
...
salesOrder.object_state = salesOrderViewModel.object_state;
_dbContext.Entry(salesOrder).State = Helpers.ConvertState(salesOrder.object_state);
_dbContext.SaveChanges();
salesOrderViewModel.document_id = salesOrder.document_id;
salesOrderViewModel.object_state = ObjectState.Unchanged;
return Json(new { salesOrderViewModel });
}
}
And a function to update states depending on the request:
public static EntityState ConvertState(ObjectState objectState){
switch (objectState){
case ObjectState.Added:
return EntityState.Added;
case ObjectState.Modified:
return EntityState.Modified;
case ObjectState.Deleted:
return EntityState.Deleted;
default:
return EntityState.Unchanged;
}
}
I understand that it is a problem with refreshing the entity state just after creating. How can I resolve that error?
You said you understand the problem... so the solution is to get the original entity from the database and update its properties directly and then update it itself. I mean what you need to do is to avoid calling
context.Update(entity);
Where entity is the object in your model.
So One solution would be something like the following which I agree it may not the best way of solving it.
Let's assume you are using generic repository (which is harder than non generic because you do not know the fields beforehand)
public void Edit(TBusinessObject entity)
{
var originalEntity = context.Set<TBusinessObject>().AsNoTracking().FirstOrDefault(r => r.Id.Equals(entity.Id));
EntityEntry<TBusinessObject> original = context.Entry(originalEntity);
EntityEntry<TBusinessObject> client = context.Entry(entity);
foreach (var property in original.OriginalValues.Properties)
{
var dbMember = original.Member(property.Name);
var clientMember = client.Member(property.Name);
if(!property.IsPrimaryKey() && dbMember.CurrentValue != clientMember.CurrentValue && clientMember.CurrentValue!= null)
{
dbMember.CurrentValue = clientMember.CurrentValue;
dbMember.IsModified = true;
}
}
context.Update(originalEntity);
context.SaveChanges(true);
}
Again, This code could be optimized and it would be way simpler if it was not a generic repository, where you know the names and the types of the fields.
Update 1:
I found out that EF.Core although not yet fully fledged with all the features that were supported by EF6. Yet it is somehow leaned towards the modern development practices.. The example that you posted was all about using EF.Core to implement the traditional repository mentality. If you switch to use UnitOfWork or CQRS you will not face these problems, changes like updates and CRUS in general will be smooth like never before. I am passing an object to the context, and the context itself is able to figure out to what table it belongs and how to handle it. Therefore I recommend changing the way you choose to utilize EF.Core
Try this simplest implementation:
public void Commit()
{
using (var context = new ApplicationDbContext())
{
context.UpdateRange(Changed);
context.AddRange(Added);
context.RemoveRange(Deleted);
context.SaveChanges();
ClearAllChanges();
}
}
Where "Changed, Added, Deleted" are just lists where you might consider AsynchronousBags

Entity Framework,Navigation properties,Repository,Unit Of Work,Changeable ORM

I am trying to create a new core framework (web mostly) with Repository and Unit Of Work pattern for my applications that i can able to change my ORM to NHibernate or Dapper later on.
Right now my interface of Unit of work is like this :
public interface IUnitOfWork : IDisposable
{
void Commit();
void Rollback();
}
And Entity Framework implementation is like this (trimmed for readability)
public class EfUnitOfWork : IUnitOfWork
{
....
public EfUnitOfWork(ApplicationDbContext context)
{
this._context = context;
this._transaction = new EfTransaction(_context.Database.BeginTransaction());
}
public void Commit()
{
this._context.SaveChanges(true);
this._transaction.Commit();
...
}
public void Rollback()
{ ...
}
}
The problem is that in my Service Layer that contains business logic i can do something like this with the navigations properties:
public bool CreateCity(CityCreateModel model)
{
using (var uow = _unitOfWorkFactory.Create())
{
var city = new City();
city.Name = model.Name;
city.State = new State() { Country = new Country() { Name = "SomeCountry" }, Name = "SomeCity" };
_cityRepository.Create(city);
try
{
uow.Commit();
return true;
}
catch (Exception)
{
uow.Rollback();
throw;
}
}
}
The repository Create method is pretty straightforward as i use entity framework :
public void Create(City entity)
{
_set.Add(entity);
}
The problem begins here , when a member of team writes a code like the Service example with using new keyword on navigation properties or adding items for collection navigation properties, entity framework detects these changes and when i save changes, these are also saved to the database.
If i try to change existing sample to Dapper.NET or to a REST service later on there can be a LOT of problems that i had to go look for every navigation property and track that they have been changed or not and write a lot of (possibly garbage) code for them as i didn't really know what is inserted on the table via entity framework and what isnt (because of navigation properties are also inserted and my repositories called once for only 1 insert that is for City in my example above)
Is there a way to prevent this behavior or is there a pattern known that i can adapt early on so i won't have problems later on?
How did you overcome this?
Before I begin I want to give some notes to your code:
public EfUnitOfWork(ApplicationDbContext context)
{
this._context = context;
this._transaction = new EfTransaction(_context.Database.BeginTransaction());
}
1) From your example I can see that you are sharing the same DbContext(given as parameter in the constuctor for the whole application. I do not think this is a good idea, because the entities will be cached in the first level cache and the change tracker will track them all. With this approach will get soon performance problems when the database will be growth.
_cityRepository.Create(city);
public void Create(City entity)
{
_set.Add(entity);
}
2) The base repository should be generic of type T where T is an entity! and so you can create a city;
var city = _cityRepository.Create();
Fill the city or provide the data as parameters in the create method.
Back to your question:
Is there a way to prevent this behavior or is there a pattern known that i can adapt early on so i won't have problems later on?
Each ORM has his own desgin concept and it is not easy to find generic way which fit to them all that way I would do the following:
1) Separate the repository contracts in one the assembly (contracts dll)
2) For each ORM Framework use a separate assembly which implement the repository contracts.
Example:
public interface ICityRepository<City> :IGenericRepsotiory<City>
{
City Create();
Find();
....
}
Entity Frmework assembly:
public class CityRepositoryEF : ICityReposiory
{
..
Dapper Frmework assembly:
public class CityRepositoryDapper : ICityReposiory
{
..
You can find a brilliant walk through if you follow the URL below. It is authored by Julie Lerman who is an entity framework evangelist.
http://thedatafarm.com/data-access/agile-entity-framework-4-repository-part-1-model-and-poco-classes/

Loaded from another DataContext?

In my previous applications when I used linq-to-sql I would always use one class to put my linq-to-sql code in, so I would only have one DataContext.
My current application though is getting too big and I started splitting my code up in different classes (One for Customer, one for Location, one for Supplier...) and they all have their own DataContext DatabaseDesignDataContext dc = new DatabaseDesignDataContext();
Now when I try to save a contact with a location (which I got from a different DataContext) I get the following error:
"An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."
I assume this is because I create a DataContext for every class, but I wouldn't know how to this differently?
I'm looking for any ideas, thanks.
My classes look like the following:
public class LocatieManagement
{
private static DatabaseDesignDataContext dc = new DatabaseDesignDataContext();
public static void addLocatie(locatie nieuweLocatie)
{
dc.locaties.InsertOnSubmit(nieuweLocatie);
dc.SubmitChanges();
}
public static IEnumerable<locatie> getLocaties()
{
var query = (from l in dc.locaties
select l);
IEnumerable<locatie> locaties = query;
return locaties;
}
public static locatie getLocatie(int locatie_id)
{
var query = (from l in dc.locaties
where l.locatie_id == locatie_id
select l).Single();
locatie locatie = query;
return locatie;
}
}
That happens if the entity is still attached to the original datacontext. Turn off deferred loading (dc.DeferredLoadingEnabled = false):
partial class SomeDataContext
{
partial void OnCreated()
{
this.DeferredLoadingEnabled = false;
}
}
You may also need to serialize/deserialize it once (e.g. using datacontractserializer) to disconnect it from the original DC, here's a clone method that use the datacontractserializer:
internal static T CloneEntity<T>(T originalEntity) where T : someentitybaseclass
{
Type entityType = typeof(T);
DataContractSerializer ser =
new DataContractSerializer(entityType);
using (MemoryStream ms = new MemoryStream())
{
ser.WriteObject(ms, originalEntity);
ms.Position = 0;
return (T)ser.ReadObject(ms);
}
}
This happens because you're trying to manage data from differing contexts - you will need to properly detach and attach your objects to proceed - however, I would suggest preventing the need to do this.
So, first things first: remove the data context instances from your entity classes.
From here create 'operational' classes that expose the CRUDs and whatnot to work with that specific type of entity class, which each function using a dedicated data context for that unit of work, perhaps overloading to accept a current context for when a unit of work entails subsequent operations.
I know everybody probably gets tired of hearing this, but you really should look at using Repositories for Data Access (and using the Unit of Work pattern to ensure that all of the repositories that are sharing a unit of work are using the same DataContext).
You can read up on how to do things here: Revisiting the Repository and Unit of Work Patterns with Entity Framework (the same concepts apply to LINQ to SQL as well).
Another solution I found for this is to create one parent class DataContext
public class DataContext
{
public static DatabaseDesignDataContext dc = new DatabaseDesignDataContext();
}
And let all my other classes inherit this one.
public class LocatieManagement : DataContext
{
public static void addLocatie(locatie nieuweLocatie)
{
dc.locaties.InsertOnSubmit(nieuweLocatie);
dc.SubmitChanges();
}
}
Then all the classes use the same DataContext.

Categories

Resources