Auto detach entity from dbcontext - c#

services.AddDbContextPool<SecurityDBContext>(options =>
options.UseSqlServer(GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"],
b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
);
This is how I added dbcontext,I used AddDbContextPool so the instance will be use over and over again for performance.
db.Entry(new AdminBlockClientConfig()
{
ActionId = input.aid.ToLongReturnZiro(),
MaxValue = input.value.ToIntReturnZiro(),
IsActive = input.isActive.ToBooleanReturnFalse(),
SiteSettingId = siteSettingId.ToIntReturnZiro()
}).State = EntityState.Added;
db.SaveChanges();
This is my code for adding new entity.
readonly SecurityDBContext db = null;
static List<AdminBlockClientConfig> AdminBlockClientConfigs = null;
public AdminBlockClientConfigService(SecurityDBContext db)
{
this.db = db;
}
This is my service constructor
services.AddScoped<IAdminBlockClientConfigService, AdminBlockClientConfigService>();
And this is my service injection config
The problem:
I don't have any validation for ActionId inside of my add new entity so if the user posts -1 for ActionId the entity will not be insert into SQL Server (relation problem) the system raises an exception and everything is as planed but the main problem is that one of the instance of SecurityDBContext become corrupted and I am no longer be able to call save change on that instance because the entity instance is still attached to dbcontext.
What is need:
It would be great if I can detach the entity after an error automatically so I can save the context.
I know how to detach entity from dbcontext, I need to its happen automatically (there is so many validation need to be added to project and I can not put time for those validation and if I put that time there will be high change of missing some place and its will be bug that can destroy my application and for performance I don't like to change the way I added dbcontext instance).
Thanks for your time.
edited: AddDbContextPool is not the problem, if one of my services add invalid data to dbcontext the other services can not use that dbcontext

Related

Entity Framework tracks change after second update

The application was built on a bunch of asp .net core mvc and entity framework.
I have a map with markers on it. I want to change the parameters of a certain object through textboxes. The request from the frontend is written in axios, and it works flawlessly. From the first time I get the changes in the database. (mysql, provider: pomelo.mysql).
When I try to access the get request for the first time, I get the old state of the object.
HttpGet request is described here:
public async Task<IEnumerable<Poi>> GetPois()
{
var pois = await _poiService.GetPois();
if (pois.Status == Domain.Enum.StatusCode.Ok)
{
return pois.Data;
}
else { return null; }
}
I have an interface that describes the necessary set of manipulations with the Poi object.
IPoiService is described here:
public interface IPoiService
{
Task<BaseResponse<IEnumerable<Poi>>> GetPois();
Task<BaseResponse<Poi>> GetPoi();
Task<BaseResponse<bool>> DeletePoi();
Task<BaseResponse<Poi>> CreatePoi();
Task<BaseResponse<Poi>> UpdatePoi(Poi entity);
}
The service for working with the Poi object is described here:
public async Task<BaseResponse<IEnumerable<Poi>>> GetPois()
{
try
{
return new BaseResponse<IEnumerable<Poi>>
{
Data = await _poiRepository.GetAll().ToListAsync(),
Status = Domain.Enum.StatusCode.Ok
};
}
catch(Exception ex)
{
return new BaseResponse<IEnumerable<Poi>>
{
Status = Domain.Enum.StatusCode.InternalServerError,
Description = $"[GetPois]: {ex.Message}"
};
}
}
BaseResponse and the corresponding interface represents the response from the database, so it doesn't affect the update problem in any way.
I also have a repository that directly implements instance operations at the database level.
The repository is described here:
public class PoiRepository : IBaseRepository<Poi>
{
private readonly ApplicationDbContext db;
public PoiRepository(ApplicationDbContext db)
{
this.db = db;
db.Database.OpenConnection();
}
public Task Create(Poi entity)
{
throw new NotImplementedException();
}
public Task Delete(Poi entity)
{
throw new NotImplementedException();
}
public IQueryable<Poi> GetAll()
{
return db.Pois;
}
public Poi Update(Poi entity)
{
db.Pois.Update(entity);
db.SaveChanges();
return entity;
}
}
Thus, I get the problem that in order to get the current data, I need to perform two HttpGet requests, and only after that EF Core will return its current value to me.
The reason that Update(entity) sends off warning bells is that you are passing entities between server and client and back. When a controller returns a View(entity) you are sending a reference entity to the view engine to build the view. The view's #Model allows you to apply bindings but it is not a client-side copy of the entity. However, when your form submit or Ajax call etc. calls back with the #model that is NOT an entity, let alone the entity the view engine was given. It will only be a copy of data and only as complete as the view bindings could populate.
So it's hard to deduce what exactly you are witnessing without stepping through the application, but my gut says you are most likely getting confused by what you think is passing entity references around. Think of it this way, in your POST actions you could accept a set of ints, strings, and such for each of the values of the model, or a completely different class definition (DTO/ViewModel) with the same fields as the entity. ASP.Net would attempt to fill in using the data submitted with a Form POST or Ajax call. By accepting an "Entity" you are just telling EF to populate the data into a new untracked entity class. It's not the same instance as a DbContext originally loaded, and the DbContext is a different instance (or should be) than when the entity was originally loaded, it isn't tracking the entity that was originally loaded.
The resulting object will only contain the details that the view happened to have stored in the individual bound controls, pieced back together behind the scenes.
My recommendation is simply to never pass entities to, and especially from a view. Use an explicit ViewModel to represent the state sent to and from a view, then in your Update method:
Fetch the actual entity using the ViewModel ID,
Check a concurrency token (RowVersionNumber / Timestamp) to ensure no changes were made to the DB since you originally fetched the data to populate the View. (optional, but recommended)
Validate the data in your view model
Copy the data from the view model into the Entity. (Automapper can help here)
SaveChanges()
No use of Update or Attach in the DbContext/DbSet.

Dynamics CRM custom workflow before entity deletion

I'm trying to make a custom workflow on Dynamics CRM. I need to delete some entities when another entity is deleted.
I created my class library and I retrieved the Guid of the deleted entity with this code:
protected override void Execute(CodeActivityContext executionContext)
{
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory =
executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service =
serviceFactory.CreateOrganizationService(context.UserId);
mService = service;
mExecutionContext = executionContext;
Guid myTipologyTypeDeleted = context.PrimaryEntityId;
bool isReading = context.PrimaryEntityName.Equals(new_tipologialettura_richiesta.EntityLogicalName);
bool isMaintenance = context.PrimaryEntityName.Equals(new_tipologiamanutenzionerichiesta.EntityLogicalName);
bool myResult = AddOnIntervention(isReading, isMaintenance, myTipologyTypeDeleted);
// Retrieve the summands and perform addition
result.Set(executionContext, myResult);
}
And here all works, I get the Guid and I get the type (reading or maintenance).
My problem is when I try to retrieve the entity with this code (the same code is working perfectly in another workflow started on record creation, but on record delete gives me error).
Entity myReadingEntity = mService.Retrieve(new_tipologialettura_richiesta.EntityLogicalName, myTipologyTypeDeleted, new ColumnSet(true));
Here I get an exception saying that no record of type MyType with id myId has been found.
I checked the record and it still exist in the DB so it has not been deleted. What I'm doing wrong?
Thanks
I think the best thing to write your custom logic here will be a Plugin, You Should write a plugin that runs on the
Message: Delete
Stage: POST
After registering the plugin on Post Delete Operation, you should add an pre-image that will be available on Post Delete with all the attributes.Instead of issuing a retrieve, the best practice is to push the required data in an image instead.
Taken from MSDN:
Registering for pre or post images to access entity attribute values results in improved plug-in performance as compared to obtaining entity attributes in plug-in code through RetrieveRequest or RetrieveMultipleRequest requests.
In your plugin change the lines of code:
Entity myReadingEntity = mService.Retrieve(new_tipologialettura_richiesta.EntityLogicalName, myTipologyTypeDeleted, new ColumnSet(true));
to
if (context.PreEntityImages.Contains("YourImageName"))
{
Entity myReadingEntity = context.PreEntityImages["YourImageName"]
}

Moving Entity framework to another project from MVC causes re-migration

I currently have an asp.net MVC 4 application which contains Entity framework 6 Code First models, DbContext and Migrations. In an attempt to separate this from my web application so I can re-use these database classes in another project I have moved all related Entity Framework classes to their own project.
However now when I run the solution it thinks my model has changed and attempts to run all my migrations once more. The problem appears to be in my use of SetInitializer as if I comment out this line I can run the web application as per normal.
public static class DatabaseConfig
{
public static void Initialize()
{
System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<G5DataContext, Configuration>());
// make sure the database is created before SimpleMembership is initialised
using (var db = new G5DataContext())
db.Database.Initialize(true);
}
}
This wasn't a problem until I've tried to move all the Entity Framework classes. Is this not possible, or have I done something fundamentally wrong?
At startup, EF6 queries exiting migrations in your database, as stored in the __MigrationHistory table. Part of this table is a context key, which includes the namespace of the entities.
If you move everything to a new namespace, EF6 doesn't recognize any of the previously run migrations, and tries to rebuild the database.
A quick solution is to run a script to rename the context key in the __MigrationHistory table to your new namespace. From http://jameschambers.com/2014/02/changing-the-namespace-with-entity-framework-6-0-code-first-databases/ :
UPDATE [dbo].[__MigrationHistory]
SET [ContextKey] = 'New_Namespace.Migrations.Configuration'
WHERE [ContextKey] = 'Old_Namespace.Migrations.Configuration'
Would also like to add that you should remember to change the ContextKey property in your Configuration class. I did the above but it was still trying to create a new database. Here's an example:
Before:
internal sealed class Configuration : DbMigrationsConfiguration<PricedNotesContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "Synapse.DAL.PricedNotesContext";
}
protected override void Seed(PricedNotesContext context)
{
}
}
After:
internal sealed class Configuration : DbMigrationsConfiguration<PricedNotesContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "SynapseDomain.DAL.PricedNotesContext";
}
protected override void Seed(PricedNotesContext context)
{
}
}
Hope this helps anyone who is stuck on this. It's a shame that it shouldn't be easier...

Using Entity Famework without a DB connection

I have an application that I tied to a DB for loading and storing data.
However I don't want to require the user to be on the network and have access to the DB. It just can't load or save without a connection.
I would like to instantiate it without a db connection (just use it in memory), but all constructors use the DB. I would prefer not to modify the entity framework generated .cs file in case I need to update it again from the DB I don't want to wipe out my changes.
How can I use the EF model without a connection?
public partial class SimRunnerEntities : ObjectContext
{
#region Constructors
/// <summary>
/// Initializes a new SimRunnerEntities object using the connection string found in the 'SimRunnerEntities' section of the application configuration file.
/// </summary>
public SimRunnerEntities() : base("name=SimRunnerEntities", "SimRunnerEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new SimRunnerEntities object.
/// </summary>
public SimRunnerEntities(string connectionString) : base(connectionString, "SimRunnerEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new SimRunnerEntities object.
/// </summary>
public SimRunnerEntities(EntityConnection connection) : base(connection, "SimRunnerEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
All SimRunnerEntities constructors use some sort of valid connection
Instantiating DbContext doesn't open connection to the database. DB connection will be opened after first query is made, where you select something or so. So if you are not modifying or reading any data, you are completely safe working without access to your database.
The thing is, that you can't really do anything with EF DbContext without triggering database, except using it's entity classes for some custom purpose.
The other question is: does you application can really work without using a database? And what are you trying to achieve?
Yes, you can do that.
In example, I have a solution which work with that architecture layout:
Project A - Server Side (WCF)
Project B - Class Library which Contain EF model + DB connection
Project C - Client side(WPF)
A reference to B,
A is WCF and hard copy the configuration from B so it can access the database using the DBContext.
C reference to B,
C is WPF project, the entities being filled from A using WCF and already have the goodies like INPC which already auto generated by EF.
The only thing I had to add in the client side is a custom class container which have list of properties of entities type (ie. List Users {get;set;} and etc..)
Further explanation according to comment request:
Q: Can you explain a bit more. If your Project B has a DB connection how does this solution work without a DB connection.
A: sure, project B configuration file (app.config) is simply not being used.
I just have it there for archive purpose or for copy paste..
I did copy and paste it to Project A app.config.
Q: In the UI I want to instantiate a data object but fails without the valid connection string because it include a EF item.
A: Regarding the instantiate an object.. simply as if you instantiate any other object: var entityX = new MyEntity{fill props code here};
It not requires me to use the Dbcontext to do that.
What I believe might be your next question is how to convoy data from WCF to client side - making the entities "trackable"? and for that I created a whole DTO Layer.. however that is beyond of your original question scope :)
Regarding the simple question can you use the Entities without using the dbcontext to manage them the answer is: Yes.

Entity Framework Code First without app.config

I hope somebody is able to help me, because it seems I'm totally stuck.
For upcoming projects in our company we'd like to use Entity Framework 5 with an code first approach. I played around a little while and everytime I try to use EF with our existing libraries, I fail because it seems EF heavily relies on an existing app.config.
In our company, we have an inhouse database library that allows us to connect to various data sources and database technologies taking the advantages of MEF (managed extensibility framework) for database providers. I just have to pass some database settings, such as host (or file), catalog, user credentials and a database provider name, the library looks for the appropriate plugin and returns me a custom connection string or IDbConnection.
We'd like to use this library together with EF because it allows us to be flexible about which database we use also change the database at runtime.
So. I saw that a typical DbContext object takes no parameters in the constructor. It automatically looks for the appropriate connection string in app.config. We don't like such things so I changed the default constructor to take a DbConnection object that get's passed to the DbContext base class. No deal.
Problems occur when the code first model changes. EF automatically notices this and looks for migration classes / configuration. But: A typical migration class requires a default parameterless constructor for the context! What a pity!
So we build our own migration class using the IDbContextFactory interface. But again, it seems that also this IDbContextFactory needs a parameterless constructor, otherwise I'm not able to add migrations or update the database.
Further, I made my own data migration configurator where I pass the context, also the target database. Problem is here: It doesn't find any migration classes, no matter what I try.
I'm completely stuck because it seems the only way to use EF is when connection strings are saved in app.config. And this is stupid because we need to change database connections at runtime, and app.config is read-only for default users!
How to solve this?
The answer is provided here
https://stackoverflow.com/a/15919627/941240
The trick is to slightly modify the default MigrateDatabaseToLatestVersion initializer so that:
the database is always initialized ...
... using the connection string from current context
The DbMigrator will still create a new data context but will copy the connection string from yours context according to the initializer. I was even able to shorten the code.
And here it goes:
public class MasterDetailContext : DbContext
{
public DbSet<Detail> Detail { get; set; }
public DbSet<Master> Master { get; set; }
// this one is used by DbMigrator - I am NOT going to use it in my code
public MasterDetailContext()
{
Database.Initialize( true );
}
// rather - I am going to use this, I want dynamic connection strings
public MasterDetailContext( string ConnectionString ) : base( ConnectionString )
{
Database.SetInitializer( new CustomInitializer() );
Database.Initialize( true );
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class CustomInitializer : IDatabaseInitializer<MasterDetailContext>
{
#region IDatabaseInitializer<MasterDetailContext> Members
// fix the problem with MigrateDatabaseToLatestVersion
// by copying the connection string FROM the context
public void InitializeDatabase( MasterDetailContext context )
{
Configuration cfg = new Configuration(); // migration configuration class
cfg.TargetDatabase = new DbConnectionInfo( context.Database.Connection.ConnectionString, "System.Data.SqlClient" );
DbMigrator dbMigrator = new DbMigrator( cfg );
// this will call the parameterless constructor of the datacontext
// but the connection string from above will be then set on in
dbMigrator.Update();
}
#endregion
}
Client code:
static void Main( string[] args )
{
using ( MasterDetailContext ctx = new MasterDetailContext( #"Database=ConsoleApplication801;Server=.\SQL2012;Integrated Security=true" ) )
{
}
using ( MasterDetailContext ctx = new MasterDetailContext( #"Database=ConsoleApplication802;Server=.\SQL2012;Integrated Security=true" ) )
{
}
}
Running this will cause the two databases to be created and migrated according to the migration configuration.
It needs a parameterless constructor in order to invoke it. What you could do is provide your default DbConntectionFactory in the empty constructor, something like:
public DbContext()
{
IDbContextFactory defaultFactory; //initialize your default here
DbContext(defaultFactory);
}
public DbContext(IDbContextFactory factory)
{
}

Categories

Resources