I have a WCF which calls EF functions.
When I invoke the method from the client to insert a user nothing happens.
This is the method I am invoking :
public void insertData(Users pUser)
{
using (var context = new AMTEntitiesContainer())
{
var User = context.Users.Add(pUser);
context.SaveChanges();
}
}
Is there anything wrong in this code?
The only thing that I see wrong is var User is not needed. Simply use context.Users.Add(pUser); and save the changes.
The other possibility is that AMTEntitiesContainer is not a proper DbContext. Without seeing your AMTEntitiesContainer class it would be hard to figure that out though.
Related
How can I prevent synchronous database access with Entity Framework Core? e.g. how can I make sure we are calling ToListAsync() instead of ToList()?
I've been trying to get an exception to throw when unit testing a method which calls the synchronous API. Are there configuration options or some methods we could override to make this work?
I have tried using a DbCommandInterceptor, but none of the interceptor methods are called when testing with an in-memory database.
The solution is to use a command interceptor.
public class AsyncOnlyInterceptor : DbCommandInterceptor
{
public bool AllowSynchronous { get; set; } = false;
public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
{
ThrowIfNotAllowed();
return result;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
ThrowIfNotAllowed();
return result;
}
public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
{
ThrowIfNotAllowed();
return result;
}
private void ThrowIfNotAllowed()
{
if (!AllowSynchronous)
{
throw new NotAsyncException("Synchronous database access is not allowed. Use the asynchronous EF Core API instead.");
}
}
}
If you're wanting to write some tests for this, you can use a Sqlite in-memory database. The Database.EnsureCreatedAsync() method does use synchronous database access, so you will need an option to enable this for specific cases.
public partial class MyDbContext : DbContext
{
private readonly AsyncOnlyInterceptor _asyncOnlyInterceptor;
public MyDbContext(IOptionsBuilder optionsBuilder)
: base(optionsBuilder.BuildOptions())
{
_asyncOnlyInterceptor = new AsyncOnlyInterceptor();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.AddInterceptors(_asyncOnlyInterceptor);
base.OnConfiguring(optionsBuilder);
}
public bool AllowSynchronous
{
get => _asyncOnlyInterceptor.AllowSynchronous;
set => _asyncOnlyInterceptor.AllowSynchronous = value;
}
}
Here are some helpers for testing. Ensure you aren't using sequences (modelBuilder.HasSequence) because this is not supported by Sqlite.
public class InMemoryOptionsBuilder<TContext> : IOptionsBuilder
where TContext : DbContext
{
public DbContextOptions BuildOptions()
{
var optionsBuilder = new DbContextOptionsBuilder<TContext>();
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
optionsBuilder = optionsBuilder.UseSqlite(connection);
return optionsBuilder.Options;
}
}
public class Helpers
{
public static async Task<MyDbContext> BuildTestDbContextAsync()
{
var optionBuilder = new InMemoryOptionsBuilder<MyDbContext>();
var context = new MyDbContext(optionBuilder)
{
AllowSynchronous = true
};
await context.Database.EnsureCreatedAsync();
context.AllowSynchronous = false;
return context;
}
}
How can I prevent synchronous database access with Entity Framework Core?
You can not. Period. THere is also no reason for this ever. You basically assume programmers using your API either are idiots or malicious - why else would you try to stop them from doing something that is legal in their language?
I have tried using a DbCommandInterceptor, but none of the interceptor methods are
called when testing with an in-memory database
There are a TON of problems with the in memory database. I would generally suggest not to use it - like at all. Unless you prefer a "works possibly" and "never actually use advanced features of the database at all". It is a dead end - we never do unit testing on API like this, all our unit tests actually are integration tests and test end to end (vs a real database).
In memory has serious no guarantee to work in anything non trivial at all. Details may be wrong - and you end up writing fake tests and looking for issues when the issue is that the behavior of the in memory database just is a little different than the real database. And let's not get into what you can do with the real database that in memory has no clue how to do to start with (and migrations also do not cover). Partial and filtered indices, indexed views are tremendous performance tools that can not be properly shown. And not get into detail differences for things like string comparisons.
But the general conclusion is that it is not your job to stop users from calling valid methods on EfCore etc. and you are not lucky to actually do that - not a scenario the team will ever support. There are REALLY good reasons at time to use synchronous calls - in SOME scenarios it seems the async handling is breaking down. I have some interceptors (in the http stack) where async calls just do not work. Like never return. Nothing I ever tried worked there - so I do sync calls when I have to (thank heaven I have a ton of caching in there).
You can prevent it at compile-time to some degree by using the Microsoft.CodeAnalysis.BannedApiAnalyzers NuGet package. More information about it here.
Methods that end up doing synchronous queries can then be added to BannedSymbols.txt, and you will get a compiler warning when attempting to use them. For example adding the following line to BannedSymbols.txt gives a warning when using First() on an IQueryable<T>:
M:System.Linq.Queryable.First`1(System.Linq.IQueryable{``0});Use async overload
These warnings can also be escalated to become compiler errors by treating warnings as errors as explained here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/errors-warnings
Unfortunately not all synchronous methods can be covered by this approach. For example since ToList() is an extension on IEnumerable<T> (and not on IQueryable<T>), banning it will not allow any use of ToList() in the same project.
I can't really find a good Google answer for you. So my suggestion in the meantime is that you start doing peer-review, aka Code Reviews and any time you find a .Tolist(), you change it to await .ToListAsync().
It's not the most high tech solution, but it does keep everyone honest, but it also allows others to become familiar with your work should they ever need to maintain it while you're booked off sick.
I create generic add method that makes search in database given id and then i make changes on table and so i save it to database but when i add it under transaction scope it didn't worked. Thrown Request timeout Exception.It works on .net framework but doesn't work on .net core.I tried Mock<> but it does give me an error that platform doesn't support that.
using (Y db = new Y())
{
using (var transaction = db.Database.BeginTransaction())
{
db.Table.Add(new Table());
db.SaveChanges();
public static void add<Y>(Func<T, bool> condition)
{
tableobject = Db.Set<Y>().where(condition).FirstOrDefault();
Db.Set<Y>().Add(tableobject);
Db.SaveChanges();
}
Transaction.Commit();
}
}
I make some changes on question for representation real situation.
I find the solution.
This is a method that takes parameters and create new instance MyDbContext.
I called this method under transaction and if you work with .net core you cant create two dbcontext instance.You can work only one dbcontext. And I passed my Dbcontext class to method and the problem solved.
I have a C# WebCore REST service that talks to a front end and uses Entity Framework to CRUD into a database. This uses Dependency Injection to add the contexts upon startup:
services.AddDbContext<FileCacheContext>(options => options.UseSqlServer(Settings.ConnectionSetting));
services.AddDbContext<FileImportContext>(options => options.UseSqlServer(Settings.ConnectionSetting));
The functionality that I have allows a user to upload a file, which is manipulated by the server and some properties of that file are returned to the front end. The uploaded file is cached in the database (FileCacheContext).
After some time has passed, the user now wishes to confirm their action and "promote" the file from the Cache (FileCacheContext) to the Import (FileImportContext); this is done by an action in the front end that contains the id of the cached file.
This parameter is passed to a different REST Controller, which is being invoked using the FileImport context, rather than the FileCache context:
public PromoteController(FileImportContext context)
{
_context = context;
}
[HttpPost]
public void Post([FromBody] int fileCacheId)
{
...
What I now need to do is to "move" this cached file from one "context" to another, something along the lines of:
var cachedFile = _context.FileCache.Where(f => f.FileCacheId == cachedFileId).FirstOrSingle();
var importedFile = new FileImport() { FileData = cachedFile.FileData };
_context.FileImport.Add(importedFile);
_context.SaveChanges();
My issue is that I can only see the one context, so I cannot get hold of the other context to either read from or write into.
The two underlying tables have no relationship, so I cannot link them in any way to load one based upon the other.
How can I get access to another (unrelated) context in EF?
So to answer my own question, I found that I could of course include the additional contexts required when instantiating the controller:
public PromoteController(FileImportContext contextImport, FileCacheContext contextCache)
{
_contextImport = contextImport;
_contextCache = contextCache;
}
And then use these as required.
This doesn't feel like the right way to do it, but if it works, it works...
I'm trying to figure out the best way to manage the DbContext. I've seen code samples that don't dispose and I've seen people say that that is a bad idea. Is it appropriate for me to do something like below? Also, should I put every transaction, including reads, in a new DbContext? This might be another question, but is the part about the EntityState necessary?
public abstract class GenericRepository<T> where T : EntityData
{
protected MyDbContext Context
{
get { return new MyDbContext(); }
}
public T Save(T obj)
{
T item;
using (var context = Context)
{
var set = context.Set<T>();
if (String.IsNullOrEmpty(obj.Id))
item = set.Add(obj);
else
{
item = set.Find(obj.Id);
item = obj;
}
// taken from another code sample
var entry = context.Entry(item);
if (entry.State == EntityState.Detached)
{
//Need to set modified so any detached entities are updated
// otherwise they won't be sent across to the db.
// Since it would've been outside the context, change tracking
//wouldn't have occurred anyways so we have no idea about its state - save it!
set.Attach(item);
context.Entry(item).State = EntityState.Modified;
}
context.SaveChanges();
}
return item;
}
}
EDIT
I also have an extended class that implements this function below. The context is not being wrapped in a using statement in this query, so I'm a little suspicious of my code.
public IQueryable<T> FindByAccountId(string accountId)
{
return from item in Context.Set<T>()
let user = UserRepository.FindByAccountId(accountId).FirstOrDefault()
where item.UserId == user.Id
select item;
}
Contexts should really be on a per request basis. The request comes in and a new context is created. This context is used for the remainder of the request then disposed of at the end of the request accordingly. This gives you the benefit of request long transactions, and as highlighted by HamidP, you also have the added benefit of cached entities; meaning that any entities loaded into the context can be loaded by retrieved without Entity Framework needing to query the database.
If you're using any kind of inversion of control container such as StructureMap then you can easily create HTTP request bound contexts by a configuration such as:
this.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<DbContext>();
You're then able to inject your DbContext (or a derivative of it) into your repository and leave your IOC container of choice to dispose of the context at the end of the request. If you were to inject the same context into another repository then you'd receive the same instance of the context.
I hope this helps!
No, it should not
Best approach here is to assign a context just for a request. you should attach a context to an incoming request and dispose your context when request is finished. In this approach you save the overhead of creating a context for every transaction and also benefit from caching mechanism of context because each context has it's inside cache and a request may access the data it had access recently.
Creating a context for each transaction is not as bad as having a long life context!! Don't ever do that, long life contexts result in many concurrency issue and the cache becomes stale and memory consumption grows high and higher and you should maintain your application in future by miracles.
Here is my scenario:
I'm using nhibernate, openning and closing the session in an IHttpModule (PreRequestHandlerExecute and PostRequestHandlerExecute).
Ninject takes care of injecting my session in all my repositories and I'm very happy with it.
Now suppose an Update to one of my entities (code simplified):
Controller:
User user = _userService.GetUser(id);
user.Name = "foo";
user.Email = "foo#bar.com";
user.Group = _groupService.GetGroup(idGroup);
if(_userService.Edit(user)) {
RedirectToAction("Index");
}
else {
return View(user);
}
Service:
if(ValidateUser(user) {
return _rep.Update(user);
}
return false;
ValidateUser is doing the validation logic and inserting any error in a IValidationDictionary that is a wrapper for the ModelState in the controller.
So far so good, I get all errors (if any) and I can show them in the view.
Here comes the problem:
When I try to save an user with an error (no name for instance), the method _rep.Update(user) is never called, but the user gets saved anyway.
Googling around it come to my knowledge the nhibernate AutoDirtyCheck, witch means that if I change an entity in memory, it will be persisted automatically in the database.
Very powerful feature I agree, but since my session is commited in PostRequestHandlerExecute, my invalid entity is saved anyway, something that I don't want.
I tried to remove this behavior using unhaddins, it worked, but then my child objects are not automatically saved when I save only the parent :(
So how to solve that?
Make ValidadeUser public and validade a copy before calling the _userService.GetUser(id)?
Put the validation logic elsewhere? Maybe in the entity class itself? (I like it so much separated!).
Thanks a lot in advance.
FYI - you can set the Nhibernate session FlushMode property to FlushMode.Never to be totally in control of when NHibernate will flush updates to the database. It might be that you could cheat and if an action is not authorized - never do a flush and the nhibernate session will die when the response is over (if the session didnt go away you really should evict the modified object, tho)
Personally, I call my validation code in mvc in the defaultmodelbinder. My viewmodel (posted data) is validated before I have done anything with it. I use one validator class for each validation concern.
public class MyController : Controller
{
public ActionResult MyActionMethod(UserChangeModel model)
{
if (!ModelState.IsValid)
{
return RedirectToAction("Index");
}
User user = _userService.GetUser(model.Id);
user.Name = model.Name;
user.Email = model.Email;
user.Group = _groupService.GetGroup(model.IdGroup);
return View(user);
}
}
public class MyDefaultModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var boundInstance = base.BindModel(controllerContext, bindingContext);
if (boundInstance != null)
{
var validator = findValidator(bindingContext.ModelType);
var errors = validator.Validate(boundinstance);
addErrorsToTheModelState(bindingContext, errors);
}
}
}
I haven't tried this myself but the first thing that comes to mind is detaching the invalid user object. If you don't doing another query that would retrieve the same user would also return the same, now invalid, object.
Fabio Maulo has a very good writeup/solution
http://fabiomaulo.blogspot.com/2009/03/ensuring-updates-on-flush.html
You can use ISession.Evict(obj) to remove the object from the session and that will prevent it from being automatically persisted. One thing to note is that this makes the object transient and attempting to load any lazy initialized child objects (typically collections) will cause NH to throw a LazyInitializationException.
ETA: I just read your comment to Maurice that you can't directly access the ISession. I manually inject the ISession into repository/service classes because it's needed for WinForms. There are several methods in ISession that I've had to access from time-to-time, particularly Evict, Merge, and Lock. I would expose the ISession or a wrapper so that you can use Evict.