I have this working in my controller, but I want to follow best practices and put my database logic in Model.
I want to put all database logic (select, update, delete, insert) to MODEL, therefore I create methods in my model.
My method for retrieving the data:
public IQueryable<ChatLogsNameViewModel> getChatLogWithName()
{
using (var db = new ChatLogContext())
{
var list = (from b in db.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list;
}
}
This is my modelView:
public class ChatLogsNameViewModel
{
public UserProfile UserProfile { get; set; }
public string Message { get; set; }
public DateTime Time { get; set; }
}
I call my getChatLogWithName() method in my controller like this:
List<ChatLogsNameViewModel> items = null;
using (var dba = new ChatLogContext())
{
items = dba.getChatLogWithName().ToList();
return View(items);
}
Error I get is:
The operation cannot be completed because the DbContext has been disposed.
What is the proper way to do this? I just want to pass collection (all records from 2 tables via join) to controller.
To ensure that the DBContext is not getting referenced after disposal. How about returning a list so you dont have to call .ToList():
public List<ChatLogsNameViewModel> getChatLogWithName()
{
using (var db = new ChatLogContext())
{
var list = (from b in db.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list.ToList();
}
}
and
items = dba.getChatLogWithName();
Or
Since it appears that dba is the same as db, couldn't you change your code to use the dba instance which won't get disposed until the end of your using statement within your controller.
public IQueryable<ChatLogsNameViewModel> getChatLogWithName()
{
var list = (from b in this.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list;
}
Lifetime - DbContext
The lifetime of the context begins when the instance is created and
ends when the instance is either disposed or garbage-collected. Use
using if you want all the resources that the context controls to be
disposed at the end of the block. When you use using, the compiler
automatically creates a try/finally block and calls dispose in the
finally block.
The problem was when the inner using got disposed, it invalidated the DbContext. So you need to use .ToList() to save the query result in memory.
Suppose getChatLogWithName is defined in the class called Repo, you can change the controller logic to something like this:
var repo = new Repo();
var items = repo.getChatLogWithName().ToList();
Or move .ToList() to getChatLogWithName.
Btw, you should not use the nested DbContexts cope, in your controller, you don't have to wrap it using another DbContextscope.
Related
I have created service which communicates with my database. GetAvailableUserId service's method cannot be run simultaneously, because I don't want to return same user's id for two different calls. So far I have managed this:
public class UserService : IUserService
{
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
transaction.Commit();
return availableUser.Id;
}
}
}
}
I wanted to test if service will work as intended, so I created simple console application to simulate synchronous requests:
Parallel.For(1, 100, (i, state) => {
var service = new UserServiceReference.UserServiceClient();
var id = service.GetAvailableUserId();
});
Unfortunately, It failed that simple test. I can see, that it returned same id for different for iterations.
Whats wrong there?
If I understood you correctly, you wan to lock method from other threads. If yesm then use lock:
static object lockObject = new object();
public class UserService : IUserService
{
public int GetAvailableUserId()
{
lock(lockObject )
{
// your code is omitted for the brevity
}
}
}
You need to spend some time and delve into the intricadies of SQL Server and EntityFramework.
Basically:
You need a database connection that handles repeatable results (which is a database connection string setting).
You need to wrap the interactions in EntityFramework within one transaction so that multiple instances do not possibly return the same result in the query and then make problems in the save.
Alternative method to achieve this is to catch DbUpdateConcurrencyException to check whether values in the row have changed since retrieving when you try to save.
So if e.g. the same record is retrieved twice. The first one to have the Available value updated in the database will cause the other one to thow concurrency exception when it tries to save because the value has changed since it was retrieved.
Microsoft - handling Concurrency Conflicts.
Add ConcurrencyCheck attribute above the Available property in your entity.
[ConcurrencyCheck]
public bool Available{ get; set; }
Then:
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
try
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
return availableUser.Id;
}
catch (DbUpdateConcurrencyException)
{
//If same row was already retrieved and updated to false, do not save, instead call the method again to get the next true row.
return GetAvailableUserId();
}
}
}
Hello, I'm currently trying to show a table in an Android base App. however, my question is regarding:
var db = new SQLiteAsyncConnection(Database.DatabasePath.DB_Path);
foreach ( var N in db.Table<Database.Inventory>() )
{
InventoryItems.Add(new Store_Listing { Id = N.Id,
Description = N.Description, Style = N.Style });
This part of the code that is taking an error of:
foreach statement cannor operate on variables of type 'AsyncTableQuery< ... > ' because 'AsyncTableQuery< ... >' does not contain a public instance for 'GetEnumerator'
foreach ( var N in db.Table<Database.Inventory>() ) "
I have looked into the Database.Inventory Class, and it is set to public class, and all the elements are exactly as the SQLite database (Type 3) table.
Honestly I don't really know what is wrong.
There are no issues connecting the database .db3, but these error is unknown to me since I'm quite new to the SQLite and Android.App Dev.
This is the code for the Database.Inventory:
using System;
using System.Collections.Generic;
using System.Text;
using SQLite;
namespace Inventory.Database
{
[Table("Inventory")]
public class Inventory
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Description { get; set; }
public string Style { get; set; }
}
}
Thank You for your feedback in advance.
You are calling a new instance of SQLiteAsyncConnection, which uses async instructions for all its methods (where needed). In the SQLite NuGet source code, you will find that Table<T> returns a variable of type AsyncTableQuery. AsyncTableQuery does not implement or inherit anything. It's a stand-alone class, which means it cannot access the method GetEnumerator that it is expecting to access for a foreach statement.
So, in your code where you have:
var db = new SQLiteAsyncConnection(Database.DatabasePath);
foreach ( var N in db.Table<Database.Inventory>() ) // Table<T> does *not* return a List<T>!!
{
InventoryItems.Add(new Store_Listing
{
Id = N.Id,
Description = N.Description,
Style = N.Style
});
}
You're trying to access what you think is a List, but is actually an AsyncTableQuery.
How to get around this
If you look at the AsyncTableQuery class, it has a ToListAsync method, which converts the AsyncTableQuery object to a List<T> object. Simply calling this will convert your Table<T> to a List<T> like so:
var db = new SQLiteAsyncConnection(Database.DatabasePath);
var myList = await db.Table<Database.Inventory>().ToListAsync();
// TODO: Add a null or empty check to prevent NullExceptions.
foreach(var N in myList)
{
// Do your stuff
}
var results = await db.Table<Database.Inventory>();
var query = conn.Table<Database.Inventory>();
query.ToListAsync().ContinueWith((t) =>
{
foreach (var N in t.Result)
{
// code from your foreach loop
}
});
I use EF6. In my BL-Layer I have following static class, working with my context, which implements DBContext:
public static class AppEnvironment
{
public static IUser CurrentUser { get; set; }
private static IKernel AppKernel { get; set; }
public static void InjectDependencies(params NinjectModule[] contextNinjectModule)
{
AppKernel = new StandardKernel(contextNinjectModule);
}
public static void Authorize(string login, string password)
{
using (var context = AppKernel.Get<ICaseContext>())
{
IUser userToBeAuthorized = context.GetAll<User>().FirstOrDefault(u => u.Login == login);
if (userToBeAuthorized != null && User.GetMD5Hash(password) == userToBeAuthorized.PasswordHash)
{
AppEnvironment.CurrentUser = userToBeAuthorized;
context.Insert(
LogRecord.CreateLogRecord(
userToBeAuthorized,
"Авторизация (успешно)",
LogAction.Read));
}
}
}
public static ICollection<CaseEntityType> GetCaseListTiny<CaseEntityType>(string queryComment) where CaseEntityType : CaseEntityBase
{
using (var context = AppKernel.Get<ICaseContext>())
{
var grantedCaseTypesIDs = AppEnvironment.CurrentUser.CaseTypesGranted.Select(casetype => casetype.ID).ToList();
var cases = context.GetAllIncluding<TaskEntityType>(AppEnvironment.CurrentUser, queryComment, LogAction.Read, t => t.CaseType
).Where(t => grantedTaskTypesIDs.Contains(t.CaseType.ID)).ToList();
return cases;
}
}
}
In my UI Layer I'm trying to use smth like that:
class Program
{
static void Main(string[] args)
{
AppEnvironment.InjectDependencies(new RealContextNinjectModule());
AppEnvironment.Authorize("UserName", "Password");
var caseList = AppEnvironment.GetCaseListTiny<RegularCase>("Get a list");
foreach (var item in caseList)
{
Console.WriteLine(item.Name);
}
}
}
But it throws ObjectContextDisposedException. Can anybody explain what's the right way of dealing with EF6 context class? How should I use it in my BLL or UIL? Why I'm not allowed to use my context class again, after it has been disposed once? I've read a lot of similar questions, but everybody says only smth about eager/lazy loading.
The reason that "everybody says only smth about eager/lazy loading" is because that is what is happening. The sequence of events is:
var caseList = AppEnvironment.GetCaseListTiny<RegularCase>("Get a list");
GetCaseTinyList then calls AppKernel.Get which returns the context into a local var context wrapped up in a using block. Within the block, it created a collection by issuing a call against the context. It does not access that list, and so the collection is not in fact populated; no SQL is run until the collection is accessed. This is lazy eveluation.
At the end of the using block, it disposes of the context. This closes the database connection and marks the context object as disposed and thus unusable.
GetCaseTinyList returns, passing the unpopulated collection back.
Finally, you run a foreach against the returned collection. At the first access the EF collection tries to run the SQL for the GetCaseTinyList against the context. Sadly, the context has been disposed (you did tell it to dispose it and so it did). This makes the system throw the error you are getting, exactly as it should.
One way you can get around this is to change GetCaseTinyCollection to access the collection; something like this:
public static ICollection<CaseEntityType> GetCaseListTiny<CaseEntityType>(string queryComment) where CaseEntityType : CaseEntityBase
{
using (var context = AppKernel.Get<ICaseContext>())
{
var grantedCaseTypesIDs = AppEnvironment.CurrentUser.CaseTypesGranted.Select(casetype => casetype.ID).ToList();
var cases = context.GetAllIncluding<TaskEntityType>(AppEnvironment.CurrentUser, queryComment, LogAction.Read, t => t.CaseType
).Where(t => grantedTaskTypesIDs.Contains(t.CaseType.ID)).ToList();
int count = cases.Count(); // or Count<T>();
return cases;
}
}
This will force the collection to be populated before the context is disposed. However it may have further shortcomings: if the entities in the collection have in turn collection of other objects then when accessed it will try to populate these sub-collections and you're back where you are now.
Another approach is not to use a using/dispose pattern. But at some point you are going to have to clean up the context so you'll have to think about how to do this.
Also, it's far easier when inserting/updating EF entities to keep the context around which you used to read the data. If you don't you have to transfer the updated entities to a new context which sounds like a pain (I've never done it that way).
Basically, you need to rethink your strategy for managing the database context.
I know that a var is only in scope within it's method. But I've came across the situation where the collection 'var' from a database connection method, needs to be accessed in a subsequent Query() method in order to make a query.
The specific error is: The name collection doesn't exist in the current context
I've been referencing the MongoDB C# driver docs in order to set up the connection and the query and all seems correct besides this issue.
Does anyone know how I can restructure my code to resolve the error?
My two methods are specified as follows in an OrderRespository class, that makes database connections and queries:
//Method to create MongoDB Orders connection and get handle on collections
public static bool CreateConnection()
{
var client = new MongoClient(connectionString);
try
{
var database = client.GetDatabase("orders");
//Get a handle on the customers collection:
var collection = database.GetCollection<BsonDocument>("customers");
}
catch(MongoConnectionException)
{
return false;
}
return true;
}
//Method to test query on database documents
public async static Task<List<Customer>> FindCustomers()
{
var documents = await collection.Find(new BsonDocument()).ToListAsync();
List<Customer> customerList = await documents.ToListAsync();
return await documents.ToListAsync();
}
And this is the customer Model POCO class that models the collection fields:
public class Customer
{
/// <summary>
/// This attribute is used to map the Id property to the ObjectId in the collection
/// </summary>
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("firstName")]
public string firstName { get; set; }
[BsonElement("lastName")]
public string lastName { get; set; }
[BsonElement("email")]
public string Email { get; set; }
}
CreateConnection should return the collection that it's creating so that the person creating the connection can actually use it:
//Consider renaming this method; you're really here to get the customers,
//not create a connection
public static YourCollectionType<BsonDocument> CreateConnection()
{
var client = new MongoClient(connectionString);
var database = client.GetDatabase("orders");
//Get a handle on the customers collection:
return database.GetCollection<BsonDocument>("customers");
}
FindCustomers can then accept the collection as a parameter:
public async static Task<List<Customer>> FindCustomers(
YourCollectionType<BsonDocument> collection)
{
var documents = await collection.Find(new BsonDocument()).ToListAsync();
List<Customer> customerList = await documents.ToListAsync();
return await documents.ToListAsync();
}
You can then use CreateConnection to create the documents that you search through:
var customers = FindCustomers(CreateConnection());
If FindCustomers is something that would only ever make sense to use with a collection created by CreateConnection and you won't ever use the created object for anything else, then you could have FindCustomer call CreateConnection directly, but odds are those conditions won't apply.
So, I am learning to make a very simple MVC + EF page. It is supposed to get a list of countries into a dropDownList. And I run into this error:
ObjectDisposedException was unhandled by user code: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I thought I was aware of that DBContext will be disposed, so I pull up the values in string and array string[]. Why do I still get this issue?
View.cshtml...
<select id="register-country">
#foreach (var country in ViewBag.Countries) { //******** ERROR HERE ********
<option value="#country[0]">#country[1]</option>
}
</select>
Controller.cs ...
public virtual ActionResult RegisterDialogPartial() {
var csm = new CountryStateModelRepository();
ViewBag.Countries = csm.GetCountries();
return PartialView(MVC.Shared.Views._RegisterDialogPartial);
}
DataAccessLayer ...
public class CountryStateModelRepository {
public IQueryable<string[]> GetCountries() {
using (var db = new CountryStateModelContainer()) {
return db.Country.Select(r => new string[] { r.CountryISO3, r.CountryName });
}
}
public IQueryable<string> GetCountryStates(string countryISO3) {
using (var db = new CountryStateModelContainer()) {
return db.CountryState.Filter(r => r.CountryISO3 == countryISO3.ToUpper()).Select(r=>r.CountryISO3).AsQueryable();
}
}
}
I am pretty sure that Entity Framework and LINQ work such that the query is not executed until the values are enumerated (and in this case, that doesn't happen until your view). If you wish to enumerate the values at the time of your function call modify your method as such:
return db.Country.Select(r => new string[] { r.CountryISO3, r.CountryName }).AsEnumerable();