Paging with Entity Framework 7 and SQL Server 2008 - asp.net-mvc

I'm trying to use paging (that is .Skip(...).Take(...) in Entity Framework 7. It works OK with Microsoft SQL Server 2012 and 2014, but fails with the following error on SQL Server 2008:
System.Data.SqlClient.SqlException (0x80131904): Incorrect syntax near 'OFFSET'. Invalid usage of the option NEXT in the FETCH statement.
I've figured out that it is a breaking change in EF version 6.1.2 (http://erikej.blogspot.com/2014/12/a-breaking-change-in-entity-framework.html). But the fix is to modify EDMX file setting ProviderManifestToken attribute to "2008".
The problem is that EF7 currently only supports code-first scenario, thus there is no any EDMX out there. The question is: how to configure ASP.NET 5 website with Entity Framework 7 to use fallback pagination approach for SQL Server older than 2012?

If you use Edmx file, you must open the edmx file using XML Editor and change
ProviderManifestToken="2012" ==> ProviderManifestToken="2008"
in line 7.
Please take a look at this blog post for more information:
http://erikej.blogspot.com.tr/2014/12/a-breaking-change-in-entity-framework.html

I encountered this problem myself using EF 7 and sql server 2008. Fortunately in the latest rc1 version of EF 7 you can solve this by using .UseRowNumberForPaging() as shown in this example:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"])
// this is needed unless you are on mssql 2012 or higher
.UseRowNumberForPaging()
);

It's broken in RC 1. Gotta wait to get RC 2.
https://github.com/aspnet/EntityFramework/issues/4616

This feature was removed in EF Core 3.x, UseRowNumberForPaging is marked as obsolete. However, you can use EfCore3.SqlServer2008Query package instead. There are 2 packages available in Nuget, one for >= .NET 5.0, other one is for >= .NET 3.1
Usage:
services.AddDbContext<MyDbContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Default"))
.ReplaceService<IQueryTranslationPostprocessorFactory, SqlServer2008QueryTranslationPostprocessorFactory>());

MyDbConnectionString is Connection string from any source
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_config["MyDbConnectionString"],
options=>
{
options.UseRowNumberForPaging();
});
}
UseRowNumberForPaging() solved issue in any case except for edmx file scenario.

You need to use something like this :
var MinPageRank = (pageIndex - 1) * pageSize + 1;
var MaxPageRank = (pageIndex * pageSize);
var person = _context.Person.FromSql($"SELECT * FROM (SELECT [RANK] = ROW_NUMBER() OVER (ORDER BY Surname),* FROM Person) A WHERE A.[RANK] BETWEEN {MinPageRank} AND {MaxPageRank}").ToList();
IQueryable<Person> PersonIQ = from s in person.AsQueryable() select s;
Person = await PaginatedList<Person>.CreateAsync(PersonIQ .AsNoTracking(), pageIndex ?? 1, pageSize, sourceFull);

Here, just set UseRowNumberForPaging() in ConfigureServices
services.AddDbContext<CallcContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Connectionstring"),opt=> { opt.UseRowNumberForPaging(); }));

Related

Trying to add AutoMapper to Asp.net Core 2?

I worked on a asp.net core 1.1 project a while ago and use in projetc AutoMapper.
in asp.net core 1.1, I add services.AddAutoMapper() in startup file :
StartUp file in asp.net core 1.1:
public void ConfigureServices(IServiceCollection services)
{
//Some Code
services.AddMvc();
services.AddAutoMapper();
}
And I use AutoMapper in Controller easily.
Controller :
public async Task<IActionResult> AddEditBook(AddEditBookViewModel model)
{
Book bookmodel = AutoMapper.Mapper.Map<AddEditBookViewModel, Book>(model);
context.books.Add(bookmodel);
context.SaveChanges();
}
And everything was fine.
But I'm currently working on a Asp.net Core 2 project and I get the error with services.AddAutoMapper() in sturtap file.
Error CS0121 The call is ambiguous between the following methods or properties: 'ServiceCollectionExtensions.AddAutoMapper(IServiceCollection, params Assembly[])' and 'ServiceCollectionExtensions.AddAutoMapper(IServiceCollection, params Type[])'
What is the reason for this error?
Also, services.AddAutoMapper in asp.net core 2 has some parameters. what should I send to this parameter?
If you are using AspNet Core 2.2 and AutoMapper.Extensions.Microsoft.DependencyInjection v6.1
You need to use in Startup file
services.AddAutoMapper(typeof(Startup));
You likely updated your ASP.NET Core dependencies, but still using outdated AutoMapper.Extensions.Microsoft.DependencyInjection package.
For ASP.NET Core you need at least Version 3.0.1 from https://www.nuget.org/packages/AutoMapper.Extensions.Microsoft.DependencyInjection/3.0.1
Which references AutoMapper 6.1.1 or higher.
AutoMapper (>= 6.1.1)
Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.0.0)
Microsoft.Extensions.DependencyModel (>= 2.0.0)
The older packages depend on Microsoft.Extensions.DependencyInjection.Abstractions 1.1.0 and can't be used with ASP.NET Core since there have been breaking changes between Microsoft.Extensions.DependencyInjection.Abstractions 1.1.0 and 2.0
In new version (6.1) of AutoMapper.Extensions.Microsoft.DependencyInjection nuget package you should use it as follows:
services.AddAutoMapper(Type assemblyTypeToSearch);
// OR
services.AddAutoMapper(params Type[] assemblyTypesToSearch);
e.g:
services.AddAutoMapper(typeOf(yourClass));
Install package:
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection -Version 7.0.0
Nuget:
https://www.nuget.org/packages/AutoMapper.Extensions.Microsoft.DependencyInjection/
In Startup Class:
services.AddAutoMapper(typeof(Startup));
None of these worked for me, I have a .NET Core 2.2 project and the complete code for configuring the mapper looks like this(part of ConfigureService() method):
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new SimpleMappings());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
Then I have my Mappings class which I've placed in the BL project:
public class SimpleMappings : Profile
{
public SimpleMappings()
{
CreateMap<DwUser, DwUserDto>();
CreateMap<DwOrganization, DwOrganizationDto>();
}
}
And finally the usage of the mapper looks like this:
public class DwUserService : IDwUserService
{
private readonly IDwUserRepository _dwUserRepository;
private readonly IMapper _mapper;
public DwUserService(IDwUserRepository dwUserRepository, IMapper mapper)
{
_dwUserRepository = dwUserRepository;
_mapper = mapper;
}
public async Task<DwUserDto> GetByUsernameAndOrgAsync(string username, string org)
{
var dwUser = await _dwUserRepository.GetByUsernameAndOrgAsync(username, org).ConfigureAwait(false);
var dwUserDto = _mapper.Map<DwUserDto>(dwUser);
return dwUserDto;
}
}
Here is a similar link on the same topic:
How to setup Automapper in ASP.NET Core
If you are using AspNet Core 2.2.Try changing your code
from:
services.AddAutoMapper();
to:
services.AddAutoMapper(typeof(Startup));
It worked for me.
In .Net 6, you can do it like
builder.Services.AddAutoMapper(typeof(Program).Assembly); // Since there is no Startup file
OR
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
Basically, it requires the assembly name as shown in screenshot below
I solved this by creating a class that inherits AutoMapper.Profile
public class model_to_resource_profile : Profile
{
public model_to_resource_profile()
{
CreateMap<your_model_class, your_model_resource_class>();
}
}
And adding this line in the Startup.cs:
services.AddAutoMapper(typeof(model_to_resource_profile ));
try this, works with 2.1 and up, i have not used any previous version so can't tell.
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
The official docs:
https://automapper.readthedocs.io/en/latest/Dependency-injection.html#asp-net-core
You define the configuration using profiles. And then you let
AutoMapper know in what assemblies are those profiles defined by
calling the IServiceCollection extension method AddAutoMapper at
startup:
services.AddAutoMapper(profileAssembly1, profileAssembly2 /*, ...*/);
or marker types:
services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2) /*, ...*/);
If you are having issue with adding your auto mapper, It is better you check through the type and version you added.
If it is not "AutoMapper.Extensions.Microsoft.DependencyInjection", then you won't be able to use "services.AddAutoMapper()".
Sometimes, you might mistakenly add "AutoMapper
Dec 6th 2019 Based upon initial attempt in a pluralsight course Building an API with ASP.NET Core by Shawn Wildermuth. As I got the error "...ambiguous 'ServiceCollectionExtensions.AddAutoMapper(IServiceCollection, params Assembly[])..."
I started researching proper syntax to implement AddAutoMapper in Core 2.2. My NuGet reference is version 7.0.0 After the tutorial had me create the Profile class in my Data repository directory which additionally referenced my model nir weiner & dev-siberia's answers above led me to trying to reference the profile class in the Startup.ConfigureServices() by name:
services.AddAutoMapper(typeof(CampProfile));
the content of the profile class is just a (no pun intended) old school map of the data class and the model in its constructor
this.CreateMap<Camp, CampModel>();
This addressed the poor lack of documentation for this current version.
Respectfully,
ChristianProgrammer

Bulk Insert with 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

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);
}
}
}

How to update breeze.js library

I could use some guidance on how to manually update between versions of Breeze and it's dependencies. I do not believe I can just update with the NuGet Package Manager.
I have been developing my Single Page App with Breeze, Knockout, WebAPI and so forth. I have been using Breeze version 0.61 and want to upgrade to the latest version so I can take advantage of the ODataActionFilters and not have to parse the Request.QueryString to pull out parameters and filters. For example when I call
var getMachineById(machineId) {
var query = EntityQuery
.from("Machines")
.where("machineId", "eq", machineId);
return manager.executeQuery(query)
.then(function (data) {
do_something_with(data.results);
})
.fail(queryFailed);
}
There has to be a way for Breeze to handle that for me, so I can just do something like this:
[AcceptVerbs("GET")]
public IQueryable<Machine> Machines()
{
return _contextProvider.Context.Machines;
}
instead of
// eg "?$filter=machineId%20eq%205"
[AcceptVerbs("GET")]
public IQueryable<Machine> Machines()
{
IQueryable<Machine> x = _contextProvider.Context.Machines;
List<ODataQueryString> list = null;
string qs = Request.RequestUri.Query.ToString(CultureInfo.CurrentCulture);
list = new ODataQueryStringParser(qs).Parse();
if (list != null)
{
int machineId = int.Parse(list[0].Value); // covert string to an int
x = x.Where(o => o.MachineId == machineId);
}
return x;
}
I notice that the Attribute decoration in the Controller has changed in the Samples. Do I need to change mine too?
namespace PilotPlantBreeze.Controllers
{
[JsonFormatter, ODataActionFilter]
public class BreezeController : ApiController
{
readonly EFContextProvider<PilotPlantDbContext> _contextProvider =
new EFContextProvider<PilotPlantDbContext>();
[AcceptVerbs("GET")]
public string Metadata()
{
return _contextProvider.Metadata();
}
[AcceptVerbs("POST")]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
... etc.
}
}
I have my 3rd party libraries in a folder ~\Scripts\lib. If I use the NuGet package manager to update, it puts all the replacements in ~\Scripts. How do I move the files into the lib folder without messing up my Team Foundation Server (Azure) source control?
Are the runtime versions of Antlr3.Runtime.dll, Breeze.WebApi.dll, Irony.dll, Newtonsoft.Json.dll, WebActivator.dll and maybe WebGrease.dll compatible between versions. I bet not. Is there something I have to change in Visual Studio?
Can I just change version entries from the package folder in packages.config?
Thanks.
I think that your best approach would be to remove any existing breeze '.js' files and the webApi and irony.dlls from your project and then simply install the latest breeze nuget package. After installing the nuget package you can go ahead and move the files to other locations within the project to match their "old" locations. I'd do the same for the NewtonSoft nuget package as well, just in case ( breeze will add this back for you). Going forward, you can just update to latest nuget and then move the files.
As you noticed you will also need to replace these attributes
[JsonFormatter, ODataActionFilter]
with this attribute
[BreezeController]
This assumes that you are not running a beta version of ASP.MVC4. I think there are posts in other forums that discuss how to migrate away from the beta.

Entity Framework 4.3.1 DBContext Connection

I'm trying to access the underlying Database and Connection object from DbContext in order to run some SQL "old style" in a library routine. This library routine uses DbContext as it can be called from many places. This worked in 4.1 however, with 4.3.1 I'm getting errors...
Here's the outline of the relevant code
using (testentities te = new testentities())
{
var result = CallGeneric(te, some parameters);
}
public static Results CallGeneric(DbContext db, some parameters);
{
var connection = (SqlConnection)db.Database.Connection;
// do some stuff here
}
In EF 4.1 this worked and I was able to use the connection directly. I updated to EF 4.3.1 using NuGet and this no longer works. Any ideas on how to get to the underlying connection?
Solution was to uninstall EF 4.1 and then re-install EF 4.3.1

Resources