I searched and could not find an answer to my question, sorry if this is a duplicate please refer me to correct solution.
I have a entity framework core setup which tracks an object 'TestRun'. I have created a list of TestRuns and wish to add them to the DbContext then write them to the database. This works fine with a regular ForEach loop but when I try to use the List.Foreach inside the using block Visual Studio suggests that the disposable object is already disposed. "Access to disposed closure".
using (var context = new TestResultsContext())
{
_testRuns.ForEach(t => context.Add(t));
context.SaveChanges();
}
I figure this has to do with the 'lazy loading' but ForEach is a void return so there is no way to force the result to enumerate.
List<T>.ForEach is not a LINQ method, that's from the List<T> class, and it's also not lazily-evaluated. So regarding the Visual Studio warning, not all warnings should be taken into account, this is one of them. The analyzers are not perfect.
You don't need to use any of those methods, however, since the proper way is use the DbSet<T>'s method AddRange:
using (var context = new TestResultsContext())
{
context.TestRuns.AddRange(_testRuns);
context.SaveChanges();
}
Related
I have a collection of users that I'm trying to read from the database, but for some reason some strange behavior takes place that I can't really figure out. Hopefully, somebody can suggest or help me find the root of this problem.
So basically, what happens is that whenever I call this code in my HomeController.cs:
var users = await _database.GetCollection<User>("ApplicationUsers").FindAsync(_ => true);
var userList = users.ToList();
it only populates userList partially, meaning only the ID and ConcurrencyStamp properties get filled, but the other properties always end up being null (as seen in: https://i.imgur.com/RTF8ljL.png)
But whenever I add this line right after the database connection initialization in the Startup.cs:
database.GetCollection<User>("ApplicationUsers");
Then suddenly userList does get populated with all the other information (as seen in https://i.imgur.com/f5IV7fh.png)
So in order for it to work, I have to get the collection right after the connection gets initialized which I'm not really fond of, because I don't have to do this for other collections. So my mongo connection code would have to look like this in the Startup.cs:
var mongoUrl = new MongoUrl(config.GetSection("DatabaseSettings:ConnectionString").Value);
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
mongoClientSettings.ClusterConfigurator = cb => ConfigureCluster(cb);
var client = new MongoClient(mongoClientSettings);
var database = client.GetDatabase(config.GetSection("DatabaseSettings:DatabaseName").Value);
database.GetCollection<User>("ApplicationUsers"); // TODO: This is needed just to let Mongo Driver know to which class to deserialize this collection
services.AddSingleton<IMongoDatabase>(database);
var pack = new ConventionPack()
{
new CamelCaseElementNameConvention(),
new IgnoreExtraElementsConvention(true),
new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)
};
ConventionRegistry.Register("DatabaseConventions", pack, t => true);
I'm guessing something happens between the execution of Startup.cs and HomeController.cs that causes the deserialization to mess up?
Update:
The same behavior seems to happen on a clean project, the only nuget packages I installed are the official mongodb driver and AspNetCore.Identity.Mongo by Matteo Fabbri. This strange deserialize behavior does not happen when I use getCollection for other classes. The problem lies with ApplicationUser which extends from MongoUser (a class made available by the AspNetCore.Identity.Mongo library)
Update 2:
Turns out that MongoUser class from the AspNetCore.Identity.Mongo library is allergic to the ConventionPack that was registered. I tested this by getting the collection before and after the registration of the database conventions. Now finding a proper solution for this.
Update 3:
Also I found out that documents are saved with properties named in Upper Camel Case, which could be the cause for mongodb driver's confusion. It seems that the conventions set to save them in CamelCase is being ignored for this particular class (ApplicationUser).
I managed to solve the problem. The issue was the order of code execution. The convention pack was registered after MongoIdentity and the database was initialized. During the initialization of MongoIdentity and the Database, the ApplicationUser was configured to not have the CamelCaseElementNameConvention in the pack, which led to the class being saved in UpperCamelCase and therefore cause confusion when retrieving the collection when the registered ConventionPack was active.
For whoever struggles with strange behavior where you may think your code should work, remember that the order of code execution is very important and could very well be the cause of the behavior. Good luck to y'all!
I'm very newbie in MVC 4 and Entity-Framework
If this question does not make sense, please let me explain better.
During the example I'm working on, I have noticed that I can make an insertion to database either using AddObject or AddToMyTableName.(my specific table name which is exist in database)
So I'm kinda confused what is the difference between these?
And which one I should use in what cases?
Here is very simplified example:
In the controller:
This is example for AddObject:
using (myProj.Models.myProjEntities db = new Models.myProjEntities())
{
myProj.Models.TestClass myTestClass = new myProj.Models.TestClass();
myTestClass.prop1 = "test1";
myTestClass.prop2 = "test2";
db.MyTable.AddObject(myTestClass);
db.SaveChanges();
}
And here is the example for AddToSpecificTable:
using (myProj.Models.myProjEntities db = new Models.myProjEntities())
{
myProj.Models.TestClass myTestClass = new myProj.Models.TestClass();
myTestClass.prop1 = "test1";
myTestClass.prop2 = "test2";
db.AddToMyTable(myTestClass);
db.SaveChanges();
}
Both of them are inserting the values to db and both are working same in my example. I'm pretty sure there are some cases when one of them is working , the other will not.
Can anyone please explain the difference?
Thanks
The AddTo<TEntity> method is now considered deprecated and has been used with EF4 and previous versions. With EF4.x the new AddObject() was introduced as replacement method for AddTo<TEntity> but for some (unknown) reason the AddTo<TEntity> method was still available even though it's was recommended to use the new AddObject() method. As of now, with EF5+ you can just use Add(). It's hard to find the exact reasons for the replacement of the methods but I guess this is most probably historical reasons and as the EF evolved so the methods were changed to reflect the new versions.
So if you consider why (if in any scenario) the one is better than the other, than just use the most common one (Of course if you don't have some version restrictions like, if you are using EF 4 for example). Otherwise you are not gaining anything and usually after some method is marked as deprecated sooner or later it's removed, so if you use AddTo<TEntity> you might need to rewrite parts of your code due to the fact that in some future version this method is no longer presented.
How would you do this (pseudo code): product1.Orders.AddRange(product2.Orders);
However, the function "AddRange" does not exist, so how would you copy all items in the EntityCollection "Orders" from product2 to product1?
Should be simple, but it is not...
The problem is deeper than you think.
Your foreach attempt fails, because when you call product1.Orders.Add, the entity gets removed from product2.Orders, thus rendering the existing enumerator invalid, which causes the exception you see.
So why does entity get removed from produc2? Well, seems quite simple: because Order can only belong to one product at a time. The Entity Framework takes care of data integrity by enforcing rules like this.
If I understand correctly, your aim here is to actually copy the orders from one product to another, am I correct?
If so, then you have to explicitly create a copy of each order inside your foreach loop, and then add that copy to product1.
For some reason that is rather obscure to me, there is no automated way to create a copy of an entity. Therefore, you pretty much have to manually copy all Order's properties, one by one. You can make the code look somewhat more neat by incorporating this logic into the Order class itself - create a method named Clone() that would copy all properties. Be sure, though, not to copy the "owner product reference" property, because your whole point is to give it another owner product, isn't it?
Anyway, do not hesitate to ask more questions if something is unclear. And good luck.
Fyodor
Based on the previous two answers, I came up with the following working solution:
public static void AddRange<T>(this EntityCollection<T> destinationEntityCollection,
EntityCollection<T> sourceEntityCollection) where T : class
{
var array = new T[sourceEntityCollection.Count()];
sourceEntityCollection.CopyTo(array,0);
foreach (var entity in array)
{
destinationEntityCollection.Add(entity);
}
}
Yes, the usual collection related functions are not there.
But,
1. Did you check CopyTo method?
2. Do you find any problem with using the iterator? You know, GetEnumerator, go through the collection and copy the entities?
The above two can solve your problems. But, I'm sure in .NET 3.0+ there would be compact solutions.
My answers are related to .NET 2.0
I am using the Dynamic Linq Library that Scott Guthrie describes here.
Scott Guthrie's examples are great and I have used the dynamic Where statements quite a bit.
Now, however, I am faced with a situation where I need to use the dynamic select functionality. Scott Guthrie shows a screenshot of this functionality (in the very last screenshot in the article) but very cleverly never explains it.
The problem is, even though the code compiles and runs, I don't see how it can possibly work in any useful manner. Perhaps with reflection?
Here is an example (remember, you must use the Dynamic Linq Library that Guthrie describes in the article above, this is not the normal Linq System.Linq).
In my sample here, I have a Users table with a UserId, FirstName, and LastName fields. But it really doesn't matter what database you use. The issue is very simple to reproduce. Here is my sample code:
First make sure you have this using statement on top:
using System.Linq.Dynamic;
Then you can run the following code:
using (DataClasses1DataContext dcdc = new DataClasses1DataContext())
{
var x = dcdc.Users.Select("new(UserId, FirstName, LastName)");
foreach (var item in x)
{
Console.WriteLine(item.ToString());
}
}
As you can see, this compiles and runs just fine. You get all your records back from the database. However, there is no way I can find to actually access the members of the new anonymous type.
Because the Select query is a string, there is no type inference at design time. So I cannot write:
Console.WriteLine(item.UserId);
The compiler has no idea that the anonymous type item has a member named UserId. So that code will not even compile (even though if you pause the debugger during the For..Each loop you will see that the debug window sees that there are UserId, FirstName and LastName members.
So... how is this supposed to work? How do you gain access to the members of the anonymous type?
It will work fine for data-binding (I suspect this is its intended use-case), which uses reflection under the hood. It will also work fine with dynamic in .NET 4.0:
foreach (dynamic item in x) {
Console.WriteLine(item.UserId);
}
Other than that... reflection or TypeDescriptor.
foreach (object item in x) {
Console.WriteLine(item.GetType().GetProperty("UserId").GetValue(item, null));
}
I have a read-only database, so I am turning off ObjectTracking (thus implicitly turning off DeferredLoading).
I wish to do lazy loading and not use LoadWith<>.
What is the simplest way to explicitly tell Linq to go and lazy fetch a relation just before I need the data itself.
For example: a simple dbml
If I have the following code:
TestDbDataContext context = new TestDbDataContext(Settings.Default.TestersConnectionString);
context.ObjectTrackingEnabled = false;
var result = context.Employees.ToList();
foreach (var employee in result)
{
// HERE Should load gift list
foreach (var gift in employee.Gifts)
{
Console.WriteLine(gift.Name);
}
}
I know I can write a full query again, but I hope we can find together a better way.
You are fighting the system... 2 thoughts:
if you know you need the other data (nested foreach), why wouldn't you want to use LoadWith? That is pretty-much the text-book use case
since you (from post) know that object tracking is required for lazy loading, why not just enable object tracking; data-contexts should usually be considered "units of work" (i.e. short-lived), so this isn't likely to hurt much in reality.
See the official replies here for why these two options (object tracking and deferred loading) are linked.
Use a LazyList:
http://blog.wekeroad.com/blog/lazy-loading-with-the-lazylist/