I use EF 4.1 Code-First ,
the problem is: EF generates all the unicode fields with N'..' prefix by default. like this :
exec sp_executesql
N'SELECT ...
FROM ...
WHERE [Title] LIKE #p__linq__0 ESCAPE N''~''',
N'#p__linq__0 nvarchar(4000)',
#p__linq__0=N'%...%'
but it cause me some problems in some characters. I want to know if there is a way to prevent EF of adding N prefix or not?
You can wrap your strings in AsNonUnicode method as mentioned at http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.asnonunicode.aspx this will generate normal strings.
Another solution would be to use CommandInterceptors and modify the resulting sql query before it gets executed.
I had similar problems with an oracle database and ODP.net provider.
AsNonUnicode didn't solved my problem.
EF 6 provides the ability to intercept the context using IDbCommandInterceptor before and after it performs the ExecuteNonQuery, ExecuteScalar, ExecuteReader operations to the database. You will need to configure the interceptor either by using config file or code-based configuration.
Config file:
<entityFramework>
<interceptors>
<interceptor type="EfSample.EfCommandInterceptor, EfSample">
</interceptor>
</interceptors>
</entityFramework>
Code-based config:
public sealed class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration ()
{
this.AddInterceptor(new EfCommandInterceptor());
}
}
Create the CommandInterceptor as shown below:
public sealed class EfCommandInterceptor
: DbCommandInterceptor
{
/// <summary>
/// Called when Reader is executing.
/// </summary>
/// <param name="command"></param>
/// <param name="interceptionContext"></param>
/// <inheritdoc />
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if(command.CommandText.Contains("N''"))
{
command.CommandText = command.CommandText.Replace("N''", "''");
}
base.ReaderExecuting(command, interceptionContext);
}
}
Related
I have an Azure Function (v3) using Entity Framework (3.0.11).
I am attempting to run the code on a TimerTrigger however injecting the database within a timer trigger does not seem to work.
Here are some (rapidly anonymized) code samples.
the CSPROJ
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AzureFunctions.Extensions.DependencyInjection" Version="1.1.3" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.11" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
a model and DBContext
namespace DataImport
{
public class Sample
{
public int SampleID { get; set; }
public string SampleField { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
public virtual DbSet<Sample> MyRecords { get; set; }
}
}
a startup class
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
[assembly: FunctionsStartup(typeof(DataImport.Startup))]
namespace DataImport
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
string con = builder.GetContext().Configuration.GetSection("ConnectionStrings:DefaultConnection").Value.ToString();
builder.Services.AddDbContext<MyDbContext>(config => config.UseSqlServer(con));
}
}
}
a Program.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using AzureFunctions.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace DataImport
{
public class Program
{
private readonly MyDbContext db;
public Program(MyDbContext database)
{
db = database;
}
[FunctionName("SampleFunction_works")]
public async Task<IActionResult> HttpRun([HttpTrigger(AuthorizationLevel.Anonymous, "GET")] HttpRequest req, ILogger log, ExecutionContext context)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
var foo = db.MyRecords.Where(c => c.SampleField == "000").FirstOrDefault();
await db.MyRecords.AddAsync(new Sample());
log.LogInformation(foo.SampleField);
return new OkObjectResult(foo);
}
[FunctionName("SampleFunction_no_work")]
public static void Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger log, ExecutionContext context)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
// tried dozens of things here, nothing works sofar.
// injecting IServiceProvider fails,
// what other ways to solve this?
// could a timer trigger perhaps make an HTTP call to the HttpRun function above?
}
}
}
when running the SampleFunction_works with a database connection we see the result of the function call as successful. Injection works within the context of an HTTP trigger. On a timertrigger however, this does not work.
I have tried a good 8 hours of different things at this point:
unsurprisingly accessing the db without injecting turns up a null property, no magic there.
adding MyDbContext to the Run function fails because it can't be injected public static void Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger log, MyDbContext db)
Microsoft.Azure.WebJobs.Host: Error indexing method 'SampleFunction_no_work'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'db' to type MyDbContext. Make sure the parameter Type is supported by the binding. If you're using binding MyDbContext(e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
doing the same as the previous but by adding IServiceProvider services to the method signature results in a similar error message, adding the line db = services.GetRequiredService<MyDbContext>(); is irrelevant if it can't get injected
some variables DO seem to be injectable in this scope ExecutionContext for example, but there doesn't seem to be anything I can use on that object.
Is there a way to:
inject a timer trigger with a database?
use a timer trigger to CALL an HTTPtrigger located within the same function?
any other solution that will allow me to access an EF database within a timertrigger context?
update:
#StevePy's comment below was correct. You can make a timertrigger's RUN method non-static and leverage the power of injection. I'd previously read that this wasn't possible, but it appears that information was out of date.
See this BLOG post for more info: https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/
Or grab this sample code to run for yourself locally:
[FunctionName("MY_FANCY_FUCNTION")]
public async Task Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger ilog, ExecutionContext context)
{
ilog.LogInformation($"TIMER EXECUTED IS DB NULL? '{db == null}'");
// note that the key part of this DOES log out as NOT NULL
// which is what we want.
return;
await Main(ilog, context);
}
Try using a non-static Run method. Many examples use a static method which can be recommended where you don't have dependencies and the method is pure. (since Functional methods should strive to be pure) See https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/ for an example of TimerTriggers /w DI.
#StevePy's comment below my questions was correct. You can make a timertrigger's RUN method non-static and leverage the power of injection. I'd previously read that this wasn't possible, but it appears that information was out of date.
See this BLOG post for more info: https://marcroussy.com/2019/05/31/azure-functions-built-in-dependency-injection/
Or grab this sample code to run for yourself locally:
[FunctionName("MY_FANCY_FUCNTION")]
public async Task Run([TimerTrigger("%TimerInterval%")] TimerInfo myTimer, ILogger ilog, ExecutionContext context)
{
ilog.LogInformation($"TIMER EXECUTED IS DB NULL? '{db == null}'");
// note that the key part of this DOES log out as NOT NULL
// which is what we want.
return;
await Main(ilog, context);
}
note that in the interests of giving credit where it is due, I'll switch the accepted answer to Steve's if and when he responds, but I'm marking as answered now to ensure that the question has an accepted answer.
I tried to inject IConfiguration into the migration (in constructor), and got exception: "No parameterless constructor defined for this object."
any workaround?
you cannot, the migrations need to be able to run outside the context of your application.
Since the Entity-framework command-line tool analyzes your code but does not run the startup.cs class.
Also it is not advisable. your migrations should be plain simple and not depend on anything. if it would, it could lead to major runtime side-effects where missing config could lead to missing tables or columns in production.
additional advise
If it involves a lot of small/equal/manual changes. Best way is to generate your migration file. Why? This way your migration will be deterministic: you know what the outcome will be. If a line in your migration fails, it is simple and clear why that is and easily(er) fixable.
There's a way to do what you want to do. In my scenario, I would like to use the database name in the connection string through the DbContext. EF core 2.1.1 is used. The code is modified from here
Create a custom MigrationsAssembly service
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using System;
using System.Reflection;
public class ContextAwareMigrationsAssembly : MigrationsAssembly
{
private readonly DbContext context;
public ContextAwareMigrationsAssembly(
ICurrentDbContext currentContext,
IDbContextOptions options,
IMigrationsIdGenerator idGenerator,
IDiagnosticsLogger<DbLoggerCategory.Migrations> logger) : base(currentContext, options, idGenerator, logger)
{
context = currentContext.Context;
}
/// <summary>
/// Modified from http://weblogs.thinktecture.com/pawel/2018/06/entity-framework-core-changing-db-migration-schema-at-runtime.html
/// </summary>
/// <param name="migrationClass"></param>
/// <param name="activeProvider"></param>
/// <returns></returns>
public override Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
{
var hasCtorWithDbContext = migrationClass
.GetConstructor(new[] { typeof(DbContext) }) != null;
if (hasCtorWithDbContext)
{
var instance = (Migration)Activator.CreateInstance(migrationClass.AsType(), context);
instance.ActiveProvider = activeProvider;
return instance;
}
return base.CreateMigration(migrationClass, activeProvider);
}
}
Replace the IMigrationAssembly service in your DbContext with your custom class
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IMigrationsAssembly, ContextAwareMigrationsAssembly>();
}
Then you can add a DbContext parameter in your migration.
public Migration20180801(DbContext context)
{
DatabaseName = context.Database.GetDbConnection().Database;
}
In your case, you can replace all the DbContext references with IConfiguration and the relevant instance in the CreateMigration override.
If it is just about your connection-string (is it?), you may want to check this answer, which basically suggests this code in your startup-project (not in your migrations-project):
var myConnectionString = Configuration.GetConnectionString(myConnectionStringName);
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(
myConnectionString ,
x => x.MigrationsAssembly(myDbContextAssemblyName)));
I have been using Entity Framework model first since VS 2010. When I build my project, EF generates a Model.Designer.cs file containing all entities. This designer file also contains the documentation added to the entities in the EDMX file.
When I created a new EF model first project in VS 2012, a Model.tt file is added to my EDMX file. This T4 template generates a single file for every entity in my model. Unfortunately, the documentation from the EDMX file is not used in the generated code.
I really like having my model documented so IntelliSense shows up when using it. The only workaround I have found so far is remove the Model.tt and the generated class files and turning the code generation on my EDMX file back on. This reverts back to the behaviour I am used from VS 2010. However, I would prefer having a separate file per entity.
Is there any way (preferably using VS tools and without having to modify any files that ship with VS) to include the documentation from the EDMX file in the generated single class files?
Edit: To further illustrate my problem, here is a quick example.
Let's say my model looks like this:
I have highlighted the part where I entered the documentation in the Properties window of the Id property.
This is what the entity looks like in the EDMX file:
<EntityType Name="Entity1">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" >
<Documentation>
<Summary>This is documentation for the ID property.</Summary>
</Documentation>
</Property>
</EntityType>
The generated class (Entity1.cs) by Model.tt looks like this:
public partial class Entity1
{
public int Id { get; set; }
}
But when I turn on the code generation for my model, this is what the entity looks like in Model.Designer.cs:
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="Model1", Name="Entity1")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Entity1 : EntityObject
{
#region Factory Method
/// <summary>
/// Create a new Entity1 object.
/// </summary>
/// <param name="id">Initial value of the Id property.</param>
public static Entity1 CreateEntity1(global::System.Int32 id)
{
Entity1 entity1 = new Entity1();
entity1.Id = id;
return entity1;
}
#endregion
#region Simple Properties
/// <summary>
/// This is documentation for the ID property.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int32 Id
{
get
{
return _Id;
}
set
{
if (_Id != value)
{
OnIdChanging(value);
ReportPropertyChanging("Id");
_Id = StructuralObject.SetValidValue(value, "Id");
ReportPropertyChanged("Id");
OnIdChanged();
}
}
}
private global::System.Int32 _Id;
partial void OnIdChanging(global::System.Int32 value);
partial void OnIdChanged();
#endregion
}
So you see: Model.Designer.cs contains my custom documentation string "This is documentation for the ID property." while Entity1.cs does not. However, Model.Designer.cs can get quite big if there are many entities and debugging into this file is somewhat slow. I'd prefer having several small files (one per entity), but still preserve the documentation from the EDMX file in the generated code.
I think you'll have to modified the T4 file. I've got the same problem and read through the T4 file a bit, and tried to follow the instruction here: http://karlz.net/blog/index.php/2010/01/16/xml-comments-for-entity-framework/
However, we're using VS 2012 and the instruction doesn't seem to work 100%. I ended up changing the property generation code at the end of the T4 file and it works exactly how I wanted it to be. The changes are in CodeStringGenerator.Property() and CodeStringGenerator.NavigationProperty()
public string Property(EdmProperty edmProperty)
{
string doc = "";
if (edmProperty.Documentation != null)
{
doc = string.Format(
CultureInfo.InvariantCulture,
"\n\t\t/// <summary>\n\t\t/// {0} - {1}\n\t\t/// </summary>\n\t\t",
edmProperty.Documentation.Summary ?? "",
edmProperty.Documentation.LongDescription ?? "");
}
return doc + string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
public string NavigationProperty(NavigationProperty navigationProperty)
{
var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());
string doc = "";
if (navigationProperty.Documentation != null)
{
doc = string.Format(
CultureInfo.InvariantCulture,
"\n\t\t/// <summary>\n\t\t/// {0} - {1}\n\t\t/// </summary>\n\t\t",
navigationProperty.Documentation.Summary ?? "",
navigationProperty.Documentation.LongDescription ?? "");
}
return doc + string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
_code.Escape(navigationProperty),
_code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
_code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
}
Note that it won't work with class documentation, so you have to do something like this with entity and complex type
<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
<#if (!ReferenceEquals(entity.Documentation, null))
{
#>
/// <summary>
/// <#=entity.Documentation.Summary#> – <#=entity.Documentation.LongDescription#>
/// </summary>
<#}#>
<#=codeStringGenerator.EntityClassOpening(entity)#>
My Seed() method is never called. It is called when I do an Update-Database from the Package Manager Console, but never when I run from code.
If I delete my database, all tables are created ( so my migration classes are executed), but my Seed() code is never called.
MVC 4, Entity Frame Work 5 Code First.
Global.asax:
protected void Application_Start()
{
Database.SetInitializer<MyContext>(new DbInitializer());
}
DBInit:
internal class DbInitializer : MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration>
{
}
DBContext:
public partial class MyContext : DbContext
{
public MyContext() : base("DefaultConnection")
{
}
// public DBSets....
}
Configuration:
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
// The constructor is actually called
AutomaticMigrationsEnabled = false;
}
protected override void Seed(MyContext context)
{
// My seed code, never called
}
What could be wrong?
So the reason was that I needed to specify my custom Initializer in the config file:
<entityFramework>
<contexts>
<context type="EFTest2.MyContext, EFTest2">
<databaseInitializer type="EFTest2.Initializers.DbInitializer, EFTest2" />
</context>
</contexts>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
After that, my Seed method is called.
Please first refer to the accepted answer.
I just want to add a very important note to this issue.
I was facing EXACTLY the same problem which described by this question (and this lead me to here). BUT I was using CreateDatabaseIfNotExists instead of MigrateDatabaseToLatestVersion and my seed method was not executed even after applying the accepted answer.
My problem was the following :
According to the documentation of the for the Seed method :
the Seed method of the DbMigrationsConfiguration will not be executed if the Database Initializer is one of the following
DropCreateDatabaseAlways
DropCreateDatabaseIfModelChanges
CreateDatabaseIfNotExists
If you are using one of those types, you should create your own class which inherits from one of those types, and then override the seed method in your own class.
In my case, adding the following class solved the problem.
public class CreateNotifierDatabaseIfNotExists : CreateDatabaseIfNotExists<NotifierContext>
{
protected override void Seed(NotifierContext context)
{
// the code of the seeding is go here
}
}
In my experience, this can happen when you stop trying to access DataContext. We had a method that was throwing errors when DataContext was finished creating the DataBase. We commented it out in order to resolve the problems with DataBase creation.
The problem was, this was the only method that actually used DataContext on initial loading. That meant that no DbInitialization logic executed, because an instance wasn't yet required.
When we added a less-problematic data retrieval logic instead of the commented-out one, the DbCreation and Seeding logic took place as normal.
I am trying to incorporate 'EF Tracing Data Provider' into an existing MVC2 app using VS2010, .NET 4.0 in order to log all SQL commands. I have no interest at this time in the caching provider. I beleive I have followed all the steps listed in the blog posting. BLOG POST My project does compile without error, however when I attempt to run the project I get the following error:
'String cannot have zero length.' The error points to Extended_JCIMS_MVC2_EF_Entities.cs Line: 25
Line 25: public ExtendedJCIMS_DevEntities(string connectionString)
Line 26: :base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
I am unable to determine what is causing this error. I assume the error is referring to the connection string from the Web.Config file. It does not like the 'connectionString' variable. I'm obviously doing something worng. I would appreciate a push in the right direction.
The relevant bits are as follows:
Web.config
--------------------------------------------------------------------------
<add name="JCIMS_DevEntities"
connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=MyServer;Initial Catalog=MyDatabase;User ID=MyUser;Password=myPassWord;MultipleActiveResultSets=True""
providerName="System.Data.EntityClient"/>
<system.data>
<DbProviderFactories>
<add name="EF Tracing Data Provider" invariant="EFTracingProvider" description="Tracing Provider Wrapper"
type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
<add name="EF Generic Provider Wrapper" invariant="EFProviderWrapper" description="Generic Provider Wrapper"
type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
</DbProviderFactories>
</system.data>
Global.ascx
-----------------------------------------------------------------------
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
//EFTracingProviderConfiguration - LOG ALL Sql commands
EFTracingProviderConfiguration.LogToFile = Server.MapPath("~/JCIMS_MVC2_EF_SQL_Logfie.txt");
}
Extended_JCIMS_MVC2_EF_Entities.cs
--------------------------------------------------------------------------
namespace JCIMS_MVC2_EF.DomainModel
{
/// <summary>
/// Partial calss that Extends the EF Datacontext Class
/// </summary>
public partial class ExtendedJCIMS_DevEntities : JCIMS_DevEntities
{
private TextWriter logOutput;
public ExtendedJCIMS_DevEntities()
: this("name=JCIMS_DevEntities")
{
}
public ExtendedJCIMS_DevEntities(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString,
"EFTracingProvider"
))
{
}
//... and more
}
}
SearchRepository.cs
------------------------------------------------------------------
public class SQLSearchRepository : ISearchRepository
{
//Database connection
private ExtendedJCIMS_DevEntities db = new ExtendedJCIMS_DevEntities(); // tracing version
public IEnumerable<SearchResults> ListAll(string strSearch, string chkSearch)
{
return (from s in db.Schools....
// and more...
}
Appreciate any assistance anyone can give me...
Have you debugged and confirmed that the connectionString passed into the ExtendedJCIMS_DevEntities method is not null or empty? That's what the error seems to indicate.