Bulk Insert with Entity Framework 6 - entity-framework-6

I'm aware that there are some commercial libraries and that there's AddRange, but AFAIK AddRange does piecemeal INSERTs under the hood.
I'm looking for a free utility that I can use to add a collection of new entities all at the same time - does one exist for EF6?

I would suggest you take a look at N.EntityFramework.Extension. It is a basic bulk extension framework for EF 6 that is available on Nuget and the source code is available on Github under MIT license.
Install-Package N.EntityFramework.Extensions
https://www.nuget.org/packages/N.EntityFramework.Extensions
Once you install it you can simply use BulkInsert() method directly on the DbContext instance. It support BulkDelete, BulkInsert, BulkMerge and more.
BulkDelete()
var dbcontext = new MyDbContext();
var orders = dbcontext.Orders.Where(o => o.TotalPrice < 5.35M);
dbcontext.BulkDelete(orders);
BulkInsert()
var dbcontext = new MyDbContext();
var orders = new List<Order>();
for(int i=0; i<10000; i++)
{
orders.Add(new Order { OrderDate = DateTime.UtcNow, TotalPrice = 2.99 });
}
dbcontext.BulkInsert(orders);

You can use the following library:
https://github.com/MikaelEliasson/EntityFramework.Utilities
It works well for simple bulk inserts and updates.
You should also look at the following post if you want to find out about other options to achieve bulk insert:
Fastest Way of Inserting in Entity Framework

Related

More graceful way to do bulk update in EF6

This is in a WPF application.
Currently I have the following code which works, but is slow:
public void IgnoreOffers(ICollection<Offer> offers)
{
using (var context = new DbContext())
{
foreach (Offer o in offers)
{
var offer = context.Offers.Find(o.Id);
offer.Ignore = true;
}
context.SaveChanges();
}
}
I know all the offers exist already in the database
Is there a more performant way of doing this?
Disclaimer: I'm the owner of the project Entity Framework Plus
This library allows you to perform BatchUpdate
context.Offers.Where(o => offers.Contains(o.Id)
.BatchUpdate(o => new Offer() { Ignore = true });
If you have too many offers, you might need to do it in multiple batch as the number of parameter in SQL is limited.
Everything will be done on the database side, so no offer will be needed to be loaded on the application side.
This method will offer you the best performance.
Disclaimer: I'm the owner of the project Entity Framework Extensions
This library is not free but offers the BulkUpdate features.
// BulkUpdate
context.BulkUpdate(offers);
// If you want to only update the `Ignore` property
context.BulkUpdate(offers, o => o.ColumnInputExpression = x => new { x.Ignore });

Using Stored procedures in ASP.net MVC?

Guys I am trying to learn MVC and I want to use stored procedures to perform all CRUD operations in MVC framework. I have googled for tutorials and everything, but all the tutorials are using that "Code-First" approach and using Entity Framework to handle all the data.
I would really appreciate if someone could help me regarding how to use SP in MVC and could provide some links to tutorials or something like that.
Before you learn Entity Framework, before you learn LINQ to SQL, take the time to learn ADO.NET which is all you need if you want to call a stored procedure. The previously mentioned technologies are in fact built on top of ADO.NET so it's good to know what they are doing. Check out lesson 7 of this tutorial which shows you exactly how to call a stored procedure from any .NET application (including MVC).
You can always use code first from your db
Code First to an Existing Database
Using this simple method, I was able to call stored procedures in MVC application
public static DataSet ExecuteDataset(string connectionString, CommandType commandType, string commandText, SqlParameter[] commandParameters)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandTimeout = 0;
command.CommandType = commandType;
command.CommandText = commandText;
if (commandParameters != null && commandParameters.Length > 0)
command.Parameters.AddRange(commandParameters);
return FillData(command, connection);
}
}
}

Calling Stored Procedure From Entity Framework that returns table with variable columns

I am using ASP.NET MVC 3 and EF 4.x
I have a procedure that returns a result set but the columns are not fixed (it may return 25 columns or may be 40 or 50).
How can I call this stored procedure from Entity Framework?
When I use function import it asks for an Entity. But I cannot select it as none of the columns is fixed.
Entity Framework is not the right tool for this. It is good at statically defined data structures, not at dynamic ones.
There are better tools for this. I would recommend Dapper, created by Marc Gravell. It's easy as pie. Just get the NuGet package and type something like
using Dapper;
using (var cnn = new SqlConnection(myConnectionString))
{
cnn.Open();
var p = new DynamicParameters();
p.Add("#params", "Id=21");
var results = cnn.Query(sql:"GetMyData",
param: p,
commandType: CommandType.StoredProcedure);
foreach(IDictionary<string, object> result in results)
{
// Do something here.
}
}
Query is a Dapper extension method, result is a DapperRow, which is a private class that implements IDictionary<string, object>, so you can just access your data as a dictionary per record.
Aside from being easy to use, it's lightning fast too.

db4o - how do I get a distinct list of classes contained in a .db4o DB file?

Say I open a .db4o file. What would be the Java code (psuedo) that would generate a list of unique Java CLASSES/TYPES contained in the database?
I am sure I could write the code to do it, but I am afraid it could be quite slow. Is there a good way to do this without querying for every object in the database? Could I use some sort of index?
I'd rather not rely on stored metadata about the database, I'd rather rely on the true information about what objects are actually stored within.
You can use something like (C# but it can be easily converted to Java :)
const string DatabaseFileName = "c:\\temp\\Learning.odb";
static void Main(string[] args)
{
using (var db = Db4oEmbedded.OpenFile(DatabaseFileName))
{
var classes = db.Ext().StoredClasses();
foreach (var #class in classes)
{
Console.WriteLine();
Console.WriteLine(#class.GetName());
foreach (var field in #class.GetStoredFields())
{
Console.WriteLine("\t{1} {0}", field.GetName(), field.GetStoredType().GetName());
}
}
}
}
Note that you have more interesting methods in ExtObjectContainer interface.
Hope this helps.

How to force only one transaction within multiple DbContext classes?

Background:
From another question here at SO I have a Winforms solution (Finance) with many projects (fixed projects for the solution).
Now one of my customers asked me to "upgrade" the solution and add projects/modules that will come from another Winforms solution (HR).
I really don't want to keep these projects as fixed projects on the existing finance solution. For that I'm trying to create plugins that will load GUI, business logic and the data layer all using MEF.
Question:
I have a context (DbContext built to implment the Generic Repository Pattern) with a list of external contexts (loaded using MEF - these contexts represent the contexts from each plugin, also with the Generic Repository Pattern).
Let's say I have this:
public class MainContext : DbContext
{
public List<IPluginContext> ExternalContexts { get; set; }
// other stuff here
}
and
public class PluginContext_A : DbContext, IPluginContext
{ /* Items from this context */ }
public class PluginContext_B : DbContext, IPluginContext
{ /* Items from this context */ }
and within the MainContext class, already loaded, I have both external contexts (from plugins).
With that in mind, let's say I have a transaction that will impact both the MainContext and the PluginContext_B.
How to perform update/insert/delete on both contexts within one transaction (unity of work)?
Using the IUnityOfWork I can set the SaveChanges() for the last item but as far as I know I must have a single context for it to work as a single transaction.
There's a way using the MSDTC (TransactionScope) but this approach is terrible and I'd reather not use this at all (also because I need to enable MSDTC on clients and server and I've had crashes and leaks all the time).
Update:
Systems are using SQL 2008 R2. Never bellow.
If it's possible to use TransactionScope in a way that won't scale to MSDTC it's fine, but I've never achieved that. All the time I've used TransactionScope it goes into MSDTC. According to another post on SO, there are some cases where TS will not go into MSDTC: check here. But I'd really prefer to go into some other way instead of TransactionScope...
If you are using multiple contexts each using separate connection and you want to save data to those context in single transaction you must use TransactionScope with distributed transaction (MSDTC).
Your linked question is not that case because in that scenario first connection do not modify data so it can be closed prior to starting the connection where data are modified. In your case data are concurrently modified on multiple connection which requires two-phase commit and MSDTC.
You can try to solve it with sharing single connection among multiple contexts but that can be quite tricky. I'm not sure how reliable the following sample is but you can give it a try:
using (var connection = new SqlConnection(connnectionString))
{
var c1 = new Context(connection);
var c2 = new Context(connection);
c1.MyEntities.Add(new MyEntity() { Name = "A" });
c2.MyEntities.Add(new MyEntity() { Name = "B" });
connection.Open();
using (var scope = new TransactionScope())
{
// This is necessary because DbContext doesnt't contain necessary methods
ObjectContext obj1 = ((IObjectContextAdapter)c1).ObjectContext;
obj1.SaveChanges(SaveOptions.DetectChangesBeforeSave);
ObjectContext obj2 = ((IObjectContextAdapter)c2).ObjectContext;
obj2.SaveChanges(SaveOptions.DetectChangesBeforeSave);
scope.Complete();
// Only after successful commit of both save operations we can accept changes
// otherwise in rollback caused by second context the changes from the first
// context will be already accepted = lost
obj1.AcceptAllChanges();
obj2.AcceptAllChanges();
}
}
Context constructor is defined as:
public Context(DbConnection connection) : base(connection,false) { }
The sample itself worked for me but it has multiple problems:
First usage of contexts must be done with closed connection. That is the reason why I'm adding entities prior to opening the connection.
I rather open connection manually outside of the transaction but perhaps it is not needed.
Both save changes successfully run and Transaction.Current has empty distributed transaction Id so it should be still local.
The saving is much more complicated and you must use ObjectContext because DbContext doesn't have all necessary methods.
It doesn't have to work in every scenario. Even MSDN claims this:
Promotion of a transaction to a DTC may occur when a connection is
closed and reopened within a single transaction. Because the Entity
Framework opens and closes the connection automatically, you should
consider manually opening and closing the connection to avoid
transaction promotion.
The problem with DbContext API is that it closes and reopens connection even if you open it manually so it is a opened question if API always correctly identifies if it runs in the context of transaction and do not close connection.
#Ladislav Mrnka
You were right from the start: I have to use MSDTC.
I've tried multiple things here including the sample code I've provided.
I've tested it many times with changed hare and there but it won't work. The error goes deep into how EF and DbContext works and for that to change I'd finally find myself with my very own ORM tool. It's not the case.
I've also talked to a friend (MVP) that know a lot about EF too.
We have tested some other things here but it won't work the way I want it to. I'll end up with multiple isolated transactions (I was trying to get them together with my sample code) and with this approach I don't have any way to enforce a full rollback automatically and I'll have to create a lot of generic/custom code to manually rollback changes and here comes another question: what if this sort of rollback fails (it's not a rollback, just an update)?
So, the only way we found here is to use the MSDTC and build some tools to help debug/test if DTC is enabled, if client/server firewalls are ok and all that stuff.
Thanks anyway.
=)
So, any chance this has changed by October 19th? All over the intertubes, people suggest the following code, and it doesn't work:
(_contextA as IObjectContextAdapter).ObjectContext.Connection.Open();
(_contextB as IObjectContextAdapter).ObjectContext.Connection.Open();
using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions{IsolationLevel = IsolationLevel.ReadUncommitted, Timeout = TimeSpan.MaxValue}))
{
_contextA.SaveChanges();
_contextB.SaveChanges();
// Commit the transaction
transaction.Complete();
}
// Close open connections
(_contextA as IObjectContextAdapter).ObjectContext.Connection.Close();
(_contextB as IObjectContextAdapter).ObjectContext.Connection.Close();
This is a serious drag for implementing a single Unit of Work class across repositories. Any new way around this?
To avoid using MSDTC (distributed transaction):
This should force you to use one connection within the transaction as well as just one transaction. It should throw an exception otherwise.
Note: At least EF6 is required
class TransactionsExample
{
static void UsingExternalTransaction()
{
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot))
{
try
{
var sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.Transaction = sqlTxn;
sqlCommand.CommandText =
#"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
sqlCommand.ExecuteNonQuery();
using (var context =
new BloggingContext(conn, contextOwnsConnection: false))
{
context.Database.UseTransaction(sqlTxn);
var query = context.Posts.Where(p => p.Blog.Rating >= 5);
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges();
}
sqlTxn.Commit();
}
catch (Exception)
{
sqlTxn.Rollback();
}
}
}
}
}
Source:
http://msdn.microsoft.com/en-us/data/dn456843.aspx#existing

Resources