What's the alternative to DataTable with ADO.NET under MVC 6? - ado.net

I am creating a MVC 6 project and I would rather use Classic ADO.net over Entity Framework 7. However It is saying that name space can not be found for both DataTable and SqlDataAdapter. I have a using System.Data and System.Data.SqlClient statement. It dose not show an error until I try to build the project.
I think I read somewhere that those two names spaces are not implemented in the new version. If so is there an alternative way of doing it or am I missing a dependency or using statement?
code:
public static DataTable GetLog(string appName)
{
DataTable table = new DataTable("ApplicationLog");
SqlDataAdapter da = null;
using (SqlConnection conn = DB.GetSqlConnection())
{
SqlCommand cmd = new SqlCommand("select * from ApplicationLog where application_name = #appname", conn);
cmd.Parameters.Add(new SqlParameter("appname", System.Data.SqlDbType.NVarChar, 100));
cmd.Parameters["appname"].Value = appName;
da = new SqlDataAdapter(cmd);
int res = da.Fill(table);
}
return table;
}
my project.json
{
"userSecretsId": "aspnet5-ASPDemo-b25bb1cc-00e6-401e-9f49-5b59c08a030f",
"version": "1.0.0-*",
"compilationOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Bestro": "1.0.0-*",
"EntityFramework.Core": "7.0.0-rc1-final",
"EntityFramework.Commands": "7.0.0-rc1-final",
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
"Microsoft.ApplicationInsights.AspNet": "1.0.0-rc1",
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final",
"Microsoft.AspNet.Diagnostics.Entity": "7.0.0-rc1-final",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
"Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final",
"Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc1-final",
"DataTables.AspNet.AspNet5": "2.0.0-beta2"
},
"commands": {
"web": "Microsoft.AspNet.Server.Kestrel",
"ef": "EntityFramework.Commands"
},
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.configuration": "4.0.0.0",
"System.Data": "4.0.0.0"
}
}
},
"exclude": [
"wwwroot",
"node_modules"
],
"publishExclude": [
"**.user",
"**.vspscc"
],
"scripts": {
"prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ]
}
}
I ended up trying a lot of different components in order to try to get it complied. If there are use references please let me know.

I understand the problem, because I like myself to use existing STORED PROCEDUREs and dynamic SQL queries in my C# code. Creating new entity for every query of the usage of some existing entity with nullable properties for saving new query results isn't a good idea. Moreover I like to use Web API to provide the data in JSON format for the usage in the frontend written in JavaScript.
On the other side Entity Framework contains the way to execute row SQL Query and to save the results in object without usage entities. I posted in the answer code fragments, which one could use. I remind shortly the idea.
One need to include connection string to the configuration file of your application. For example you can create appsettings.json with
{
"Data": {
"MyDbConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;MultipleActiveResultSets=true",
}
}
Then you create dummy context class
public class MyDbContext : DbContext
{
}
without any entity. You will use the DbContext only for holding the database connection.
Then you write Startup.cs which construct Configuration property based on the content of "appsettings.json".
public class Startup
{
// property for holding configuration
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables();
// save the configuration in Configuration property
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
});
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options => {
options.UseSqlServer(Configuration["Data:MyDbConnectionString"]);
});
}
...
}
Now you have MyDbContext injected and one can use it in context and you can use
using (var cmd = MyDbContext.Database.GetDbConnection().CreateCommand()) {
cmd.CommandText = "select * from ApplicationLog where application_name = #appname";
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
cmd.Parameters.Add(new SqlParameter("#appname", SqlDbType.NVarChar, 100) {
Value = appName
});
da = new SqlDataAdapter(cmd);
int res = da.Fill(table);
}
I personally prefer to use List<dynamic> and ExecuteReader/ExecuteReaderAsync instead of DataTable (see the code from the answer), but it's not so important.

You should post your project.json.
Referencing System.Data and removing dnxcore50 in project.json lets me compile the above code:
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.Data": "4.0.0.0"
}
}
}

Oleg's post was very helpful. Looks like with the latest release the DbContext will need a constructor that takes a DbContextOptions if you're using the built-in DI. There are a couple different flavors, but you can't just have a "dummy" empty DbContext class anymore.
https://docs.efproject.net/en/latest/miscellaneous/configuring-dbcontext.html

Related

How to map only changed properties using Automapper and Entity Framework

I know that this question had been asked already, but those answers didn't work for me. So, I beg not to close this question! 😎
The goal is simple:
Get entity from database using Entity Framework.
Pass this entity to MVC view.
Edit entity on the page.
Save entity.
What I have:
Entities
• Client relates to table in database.
• ClientDto is the same as Client, but without Comment property (to emulate situation when user must not see this field).
public class Client
{
public string TaskNumber { get; set; }
public DateTime CrmRegDate { get; set; }
public string Comment { get; set; }
}
public class ClientDto
{
public string TaskNumber { get; set; }
public DateTime CrmRegDate { get; set; }
}
Database table
CREATE TABLE dbo.client
(
task_number varchar(50) NOT NULL,
crm_reg_date date NOT NULL,
comment varchar(3000) NULL
);
-- Seeding
INSERT INTO dbo.client VALUES
('1001246', '2010-09-14', 'comment 1'),
('1001364', '2010-09-14', 'comment 2'),
('1002489', '2010-09-22', 'comment 3');
MVC controller
public class ClientsController : Controller
{
private readonly ILogger logger;
private readonly TestContext db;
private IMapper mapper;
public ClientsController(TestContext db, IMapper mapper, ILogger<ClientsController> logger)
{
this.db = db;
this.logger = logger;
this.mapper = mapper;
}
public IActionResult Index()
{
var clients = db.Clients;
var clientsDto = mapper.ProjectTo<ClientDto>(clients);
return View(model: clientsDto);
}
public async Task<IActionResult> Edit(string taskNumber)
{
var client = await db.Clients.FindAsync(taskNumber);
return View(model: client);
}
[HttpPost]
public async Task<IActionResult> Edit(ClientDto clientDto)
{
// For now it's empty - it'll be used for saving entity
}
}
Mappings:
builder.Services
.AddAutoMapper(config =>
{
config.CreateMap<Client, ClientDto>();
config.CreateMap<ClientDto, Client>();
}
Helper (outputs entity entry as JSON)
internal static class EntititesExtensions
{
internal static string ToJson<TEntity>(this EntityEntry<TEntity> entry) where TEntity: class
{
var states = from member in entry.Members
select new
{
State = Enum.GetName(member.EntityEntry.State),
Name = member.Metadata.Name,
Value = member.CurrentValue
};
var json = JsonSerializer.SerializeToNode(states);
return json.ToJsonString(new JsonSerializerOptions { WriteIndented = true });
}
}
THE PROBLEM
I need to map only changed properties from ClientDto to Client in Edit(ClientDto clientDto).
Which solutions were used
Solution 1
Just map ClientDto to Client:
[HttpPost]
public async Task<IActionResult> Edit(ClientDto clientDto)
{
var client = mapper.Map<Client>(clientDto);
logger.LogInformation(db.Entry(client).ToJson());
db.Update(client);
await db.SaveChangesAsync();
return View(viewName: "Success");
}
The problem with this code is that absolutely new Client entity gets created which properties will be filled by ClientDto. This means that Comment property will be NULL and it will make NULL the column comment in database. In general case, all hidden properties (i.e. absent in DTO) will have their default values. Not good.
Output:
[
{
"State": "Detached",
"Name": "TaskNumber",
"Value": "1001246"
},
{
"State": "Detached",
"Name": "Comment",
"Value": null
},
{
"State": "Detached",
"Name": "CrmRegDate",
"Value": "2010-09-15T00:00:00"
}
]
Solution 2
I tried to use the solution from the answer I mentioned above:
public static IMappingExpression<TSource, TDestination> MapOnlyIfChanged<TSource, TDestination>(this IMappingExpression<TSource, TDestination> map)
{
map.ForAllMembers(source =>
{
source.Condition((sourceObject, destObject, sourceProperty, destProperty) =>
{
if (sourceProperty == null)
return !(destProperty == null);
return !sourceProperty.Equals(destProperty);
});
});
return map;
}
In configuration:
builder.Services
.AddAutoMapper(config =>
{
config.CreateMap<Client, ClientDto>();
config.CreateMap<ClientDto, Client>().MapOnlyIfChanged();
});
Running same code as in solution 1, we get the same output (Comment is null):
[
{
"State": "Detached",
"Name": "TaskNumber",
"Value": "1001246"
},
{
"State": "Detached",
"Name": "Comment",
"Value": null
},
{
"State": "Detached",
"Name": "CrmRegDate",
"Value": "2010-09-15T00:00:00"
}
]
Not good.
Solution 3
Let's take another route:
Obtain entity from database.
Use non-generic Map in order to overwrite the values from ClientDto to Client from database.
Tune mapping configuration in order to skip properties which values are equal.
builder.Services
.AddAutoMapper(config =>
{
config.CreateMap<Client, ClientDto>();
config.CreateMap<ClientDto, Client>()
.ForMember(
dest => dest.TaskNumber,
opt => opt.Condition((src, dest) => src.TaskNumber != dest.TaskNumber))
.ForMember(
dest => dest.TaskNumber,
opt => opt.Condition((src, dest) => src.CrmRegDate != dest.CrmRegDate));
});
[HttpPost]
public async Task<IActionResult> Edit(ClientDto clientDto)
{
var dbClient = await db.Clients.FindAsync(clientDto.TaskNumber);
logger.LogInformation(db.Entry(dbClient).ToJson());
mapper.Map(
source: clientDto,
destination: dbClient,
sourceType: typeof(ClientDto),
destinationType: typeof(Client)
);
logger.LogInformation(db.Entry(dbClient).ToJson());
db.Update(dbClient);
await db.SaveChangesAsync();
return View(viewName: "Success");
}
This finally works, but it has problem - it still modifies all properties. Here's the output:
[
{
"State": "Modified",
"Name": "TaskNumber",
"Value": "1001246"
},
{
"State": "Modified",
"Name": "Comment",
"Value": "comment 1"
},
{
"State": "Modified",
"Name": "CrmRegDate",
"Value": "2010-09-15T00:00:00"
}
]
So, how to make Automapper update only modified properties? 😐
You do not need to call context.Update() explicitly.
When loading entity, EF remember every original values for every mapped property.
Then when you change property, EF will compare current properties with original and create appropriate update SQL only for changed properties.
For further reading: Change Tracking in EF Core

IBM ICN 3.0.3 - Ugly display of a ChoiceList for a mono-valued Property in the Content dialog

Given a Document class, a mono-valued Property of the Entry Template is associated with a ChoiceList. This works well if the ChoiceList has no "sublevels" (Choice).
When a Group Choice is added and the user tries to fill the property, the dialog becomes ugly, as well as displayed below:
Is there a way to automatically unfold the tree view for the root Choices, and moreover to remove the "none" label ("Aucun" in french) as well as the symbolic name of the ChoiceList (blurred here)?
Do I have to write a Plugin to fix the issue?
Update. The purpose of "Aucun" here is to empty the field.
I contacted the support team, and in a few words, it's not possible "out of the box". But I found a workaround.
I wrote a ResponseFilter which catches the response of the request /p8/openContentClass. Turns out its response contains the ChoiceList values:
{
"classes": [{
"parentClassId": "<PARENTCLASSID>",
"template_name": "<ENTRYTEMPLATE>",
/* [...] */
}
],
/* [...] */
"criterias": [/* [...] */, {
"settability": "readWrite",
"defaultOperator": "EQUAL",
"minValue": null,
"uniqueValues": true,
"orderable": false,
"choiceList": {
"choices": /* <----- here */,
"displayName": "CL_ToFilter"
},
/* [...] */
"name": "<propertyName>"
}
]
}
Reformatting "choices" entry to get a one-level Choice List ensure a display on one level. Below the relevant code of the ResponseFilter:
public class ChoiceListValuesResponseFilter extends PluginResponseFilter {
public String[] getFilteredServices() {
return new String[] { "/p8/openContentClass"/* "/p8/openItem"*/ };
}
public void filter(String serverType, PluginServiceCallbacks callbacks,
HttpServletRequest request, JSONObject jsonResponse) throws Exception {
// [...]
JSONArray jsonProperties =
(JSONArray) jsonResponse.get("criterias");
Iterator it = jsonProperties.iterator();
while (it.hasNext()) {
JSONObject jo = (JSONObject) it.next();
if ("<PROPERTYWITHFILTEREDCL>".equals(jo.get("name"))) {
JSONObject choiceListJo = (JSONObject) jo.get("choiceList");
// do the processing here
break;
}
}
}
// [...]
}

kendo ui grid not working with .net core

I am trying to get Kendo UI grid MVC working in a .net core, reading data from PostgreSQL database.
I set up a project, connected it to a database, scaffolded controller with appropriate views and it's working ok (meaning CRUD operations are working ok).
Now I want to hook it up with Kendo.
I followed guide from kendo website and installed successfully (although it didn't offer pro version in nugget so I had to install trial??). I added all those CSS/js files in _Layout.cshtml
this is my code in controller:
public ActionResult kendo()
{
return View();
}
// GET for Kendo
public ActionResult Categories_Read([DataSourceRequest] DataSourceRequest request)
{
return Json(GetCategories().ToDataSourceResult(request));
}
public static IEnumerable<Category> GetCategories()
{
var result = from c in _context.Categories
select new Category()
{
Id = c.Id,
Name = c.Name
};
return result;
}
this is my code in kendo.cshtml
#(Html.Kendo().Grid<Category>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.Id).Title("id");
columns.Bound(c => c.Name);
})
.HtmlAttributes(new { style = "height: 550px;" })
.Scrollable()
.Groupable()
.Sortable()
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Categories_Read", "Categories"))
.PageSize(20)
)
Model is simple
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
The problem is that there is no data displayed in kendo grid. I can see in debugger that there is request going forward and data coming backwards but nothing is shown in grid.
?
The problem is most probably caused by the new ASP.NET Core serialization mechanism. Follow the guidelines in the "second" Step 4 here:
http://docs.telerik.com/kendo-ui/aspnet-mvc/mvc-core/getting-started
Step 4 Open Startup.cs, using a text editor (IDE) and update it as described below.
Locate the ConfigureServices method and add a call to services.AddKendo at the end.
public void ConfigureServices(IServiceCollection services)
{
...
// Maintain property names during serialization. See:
// https://github.com/aspnet/Announcements/issues/194
services
.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
// Add Kendo UI services to the services container
services.AddKendo();
}
Locate the Configure method and add a call to app.UseKendo at the end.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
// Configure Kendo UI
app.UseKendo(env);
}
UPDATE
In order to verify that the above configuration is applied, check the server response and see if it has the following structure and letter casing for Data and Total:
{
"Data": [
{ "Id": "1", "Name": "Name 1" },
{ "Id": "2", "Name": "Name 2" }
],
"Total": "2"
}
I had the same problem in a .net core 3.1 project, the solution here is to add the following lines in Startup.cs:
services
.AddMvc()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
services.AddKendo();

Asp.net 5.0 MVC6 EF6 Migration Scripts

I am working on Asp.net 5.0 mvc6 and I am wanting to use entityframework 6 because 7 isn't completely coded yet and I have been able to get it to do everything but migration.
When I type enable-migration, add-migration or update-datatabase I get
enable-migration : The term 'enable-migration' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of
the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ enable-migration
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (enable-migration:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I'm pretty sure these commands are not in powershell. I did however find that there are tools in %userdir%.dnx\packages\EntityFramework\6.1.3\tools. Researching migrate.exe a little bit the little I did find they told me that it only works with proj files and so doesn't work with the new setup.
I have also tried to go the programming route with this code in the constructor of dbcontext I have:
var configuration = new MigrationConfiguration();
var migrator = new DbMigrator(configuration);
migrator.Configuration.TargetDatabase = new DbConnectionInfo(nameOrConnectionString, "System.Data.SqlClient");
if (migrator.GetPendingMigrations())
{
migrator.Update();
}
and this code in my confirmation script:
public MigrationConfiguration()
{
AutomaticMigrationsEnabled = true;
MigrationsAssembly = Assembly.GetAssembly(typeof(InitialCreate));
MigrationsNamespace = "[Namespace of my migration scripts]";
}
Before I made any of this the database made a __migrationHistory table with an 201509291902537_InitialCreate in it, so I made one file with that name and made another called 201509291902538_test they both look like this:
201509291902537_InitialCreate:
namespace Infrastructure.EF.Migrations
{
using System.Data.Entity.Migrations;
public partial class InitialCreate : DbMigration
{
public override void Up()
{
}
}
}
201509291902538_test:
namespace Infrastructure.EF.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class test: DbMigration
{
public override void Up()
{
Sql("insert into LegalEntity (Id, Name ) values(" + Guid.NewGuid() + ", 'Test'");
}
}
}
No matter what I tried migrator.GetPendingMigrations() never says it has any new updates and if I made a fake update and tell it exactly what it needs to update it still doesn't work it just throws a null reference exception on the update function.
Here's my fake migrator:
namespace Infrastructure.EF.Contexts
{
public class Migrator : DbMigrator
{
public Migrator(DbMigrationsConfiguration configuration) : base(configuration)
{
}
public override IEnumerable<string> GetDatabaseMigrations()
{
return new List<string>() { "InitialCreate" };
}
public override IEnumerable<string> GetLocalMigrations()
{
return new List<string>() { "InitialCreate", "test" };
}
public override IEnumerable<string> GetPendingMigrations()
{
return new List<string>() { "test" };
}
}
}
project.json:
{
"version": "1.0.0-*",
"dependencies": {
"Autofac.Framework.DependencyInjection": "4.0.0-beta5-*",
"AutoMapper": "4.0.4",
"EntityFramework": "6.1.3",
"log4net": "2.0.3",
"Microsoft.AspNet.Http.Abstractions": "1.0.0-beta5",
"Microsoft.CSharp": "4.0.0-*",
"Microsoft.Framework.Configuration.Json": "1.0.0-beta5",
"RestSharp": "105.2.3",
"System.Linq": "4.0.0-*",
"System.Runtime": "4.0.10-*",
"System.Threading": "4.0.10-*"
},
"frameworks": {
"dnx451": {
}
},
"configurations": {
"DV": { },
"QA": { },
"TR": { },
"PR": { }
}
}
I have tried it with both the class name and the file name and neither seems to work.
Has anybody had any luck with any of these things or see what I'm doing wrong with one of them?
If you want to use EF6 with ASP.NET MVC6, then expose EF6 repositories as Web API 2 in .NET 4.5 and consume them in ASP.NET MVC6.
I know its lots of additional work, I followed this approach to learn and develop MVC6 applications because of existing EF6 data access layer and similar issues in EF6 to EF7 migrations.
Have you tried this: http://www.bricelam.net/2014/09/14/migrations-on-k.html ?
It's for EF 7, but I think you could just change the version to EF6.

Using WebClient in ASP.NET 5

I am working in the VS15 beta and trying to use WebClient. While System.Net is referenced, and the intellisense suggests the WebClient class is available, on build I get the following error:
The type or namespace name 'WebClient' does not exist in the namespace 'System.Net' (are you missing an assembly reference?) MyProj.ASP.NET Core 5.0 HomeController.cs
I am doing the following simplistic code:
var client = new System.Net.
var html = client.DownloadString(url);
When I go to the definition of Web Client, it shows me the source. Not quite sure what the issue is - is WebClient moved? I am struggling to find the resolution.
Thanks!
Not sure about WebClient, but you can use System.Net.Http.HttpClient to make web requests as well.
Add these references to the project.json:
"frameworks": {
"aspnet50": {
"frameworkAssemblies": {
"System.Net.Http": "4.0.0.0"
}
},
"aspnetcore50": {
"dependencies": {
"System.Net.Http": "4.0.0-beta-*"
}
}
},
And then here's how to call it from an MVC 6 action method:
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
namespace WebApplication50.Controllers
{
public class HomeController : Controller
{
public async Task<IActionResult> Index()
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MyClient", "1.0"));
var result = await httpClient.GetStringAsync("http://www.microsoft.com");
...
return View();
}
}
}
You can still use WebClient if you only target full .NET Framework instead of .NET Core by in your project.json changing:
"frameworks": {
"dnx451": { },
"dnxcore50": { }
},
to
"frameworks": {
"dnx451": { }
},