Error CS1061 'DbContextOptionsBuilder' does not contain a definition for 'UseMySql' - mysql

I just learn to use ASP.NET CORE 2.0 MVC using Visual Studio Community Edition.
I want to use MySQL database instead of using SQL Server because I need to use some data inside the old MySQL DB. Please help me solving this problem .. thank you
Here is my error:
Severity Code Description Project File Line Suppression State
Error CS1061 'DbContextOptionsBuilder' does not contain a definition
for 'UseMySql' and no extension method 'UseMySql' accepting a first
argument of type 'DbContextOptionsBuilder' could be found (are you
missing a using directive or an assembly
reference?) LearnEFCore d:\temp\aspnet\LearnEFCore\LearnEFCore\Startup.cs 29 Active
My code as follow:
In Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using LearnEFCore.Data;
....
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var sqlConnectionString = Configuration.GetConnectionString("DataAccessMySqlProvider");
services.AddDbContext<SchoolContext>(options => options.UseMySql(sqlConnectionString));
services.AddMvc();
}
In my appsettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"DataAccessMySqlProvider": "server=localhost;port=3306;userid=root;password=root;database=sportstore;"
}
}
In my Models/Data
using LearnEFCore.Models;
using Microsoft.EntityFrameworkCore;
namespace LearnEFCore.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
My csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
<PackageReference Include="MySql.Data.EntityFrameworkCore" Version="6.10.4" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
</ItemGroup>
</Project>

changin to Pomelo.EntityFrameworkCore.MySql solved the problem ...

Related

Unable to parse json file in SourceGenerator

I am attempting to read a json file and generate C# code. I have a source generator like below, and if I uncomment the JsonDocument line, the source generator stops working. In fact, if I use any class from System.Text.Json it stops working. I have also attempted to use Newtonsoft.Json same result.
Whilst building the target project that is using this source generator I get a build error error MSB6006: "csc.exe" exited with code -532462766.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text.Json;
namespace Raya.Plugin.Registrations.SourceGenerator
{
[Generator]
public class DeviceRegistrationGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var metadata = context.AdditionalFiles.Single(x => x.Path.EndsWith("plugin.metadata.json"));
// var doc = JsonDocument.Parse(metadata.GetText(context.CancellationToken).ToString());
}
public void Initialize(GeneratorInitializationContext context)
{
#if DEBUG
if (!Debugger.IsAttached)
{
Debugger.Launch();
}
#endif
Debug.WriteLine("Initalize code generator");
}
}
}
Generator csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DevelopmentDependency>true</DevelopmentDependency>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" Version="6.0.5" GeneratePathProperty="true" PrivateAssets="all"/>
</ItemGroup>
</Project>
UPDATE
Adding following seems to make it a work a bit, but I am not sure what is happening. Now I am getting InvalidCastException
<PropertyGroup>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<Target Name="GetDependencyTargetPaths" AfterTargets="ResolvePackageAssets">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="#(ResolvedCompileFileDefinitions)" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
Exception
Since the project using this source generator also ends up referencing System.Text.Json.SourceGenerator.dll, I am getting following exception from that dll.
Unhandled Exception: System.InvalidCastException: [A]Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax cannot be cast to [B]Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax. Type A originates from 'Microsoft.CodeAnalysis.CSharp, Version=4.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn\Microsoft.CodeAnalysis.CSharp.dll'. Type B originates from 'Microsoft.CodeAnalysis.CSharp, Version=4.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' in the context 'LoadFrom' at location 'C:\Users\myuser.nuget\packages\microsoft.codeanalysis.csharp\4.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll'

BIML SSIS ScriptTask as a data source - Error with OutputBuffer

I am getting the below error when trying to generate a package through BIML using a ScriptTask as a datasource. I have a large (circa 5GB) XML file to load and wanted to use a StreamReader to get the data into the database.
'Output0Buffer' does not contain a definition for 'PORTF_LIST' and no extension method 'PORTF_LIST' accepting a first argument of type 'Output0Buffer' could be found (are you missing a using directive or an assembly reference?).
This is occurring for each column. The columns are dynamic and come from a separate method in a c# class looking at the dacpac so should be the same names and casing everywhere.
Sample of the file as below:
<ANALYTICS>
<INSTRUMENTS ASOF_DATE="3/31/2017" CREATE_DATE="4/2/2017" RECORDS="3763">
<INSTRUMENT>
<PORTF_LIST>XX1245897</PORTF_LIST>
<PRT_FULL_NAME>Convertible Bonds</PRT_FULL_NAME>
<ISIN>11803384</ISIN>
</INSTRUMENT>
</INSTRUMENTS>
</ANALYTICS>
Output buffer is defined as below (there are 250 odd columns, but all follow the same pattern:
<OutputBuffers>
<OutputBuffer Name="Output0" IsSynchronous="false">
<Columns>
<Column Name="PORTF_LIST" DataType="String" Length="255"/>
<Column Name="PRT_FULL_NAME" DataType="String" Length="255"/>
<Column Name="ISIN" DataType="String" Length="255"/>
</Columns>
</OutputBuffer>
</OutputBuffers>
The script task code where I am trying to add to the buffer is below:
<## property name="Elements" type="String" #>
<## property name="Columns" type="String" #>
<## property name="BufferColumns" type="String" #>
<## property name="RootElement" type="String" #>
<ScriptComponentProject ProjectCoreName="SC_eb1debcd2374468ebccbbfad4fbe5976" Name="XmlSource">
<AssemblyReferences>
<AssemblyReference AssemblyPath="Microsoft.SqlServer.DTSPipelineWrap" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.DTSRuntimeWrap" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.PipelineHost" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.TxScript" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.ManagedDTS.dll" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.ScriptTask.dll" />
<AssemblyReference AssemblyPath="System.dll" />
<AssemblyReference AssemblyPath="System.AddIn.dll" />
<AssemblyReference AssemblyPath="System.Data.dll" />
<AssemblyReference AssemblyPath="System.Windows.Forms.dll" />
<AssemblyReference AssemblyPath="System.Xml.dll" />
<AssemblyReference AssemblyPath="System.Xml.Linq.dll" />
<AssemblyReference AssemblyPath="System.Core.dll" />
</AssemblyReferences>
<OutputBuffers>
<!--
Define what your buffer is called and what it looks like
Must set IsSynchronous as false. Otherwise it is a transformation
(one row enters, one row leaves) and not a source.
-->
<OutputBuffer Name="Output0" IsSynchronous="false">
<Columns>
<#=BufferColumns#>
</Columns>
</OutputBuffer>
</OutputBuffers>
<Files>
<File Path="Properties\AssemblyInfo.cs">
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("XmlSource")]
[assembly: AssemblyDescription("Script Component as source")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("XmlSource")]
[assembly: AssemblyCopyright("Copyright # 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.*")]
</File>
<File Path="main.cs">
<![CDATA[
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Security;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Windows.Forms;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void PreExecute()
{
base.PreExecute();
}
public override void PostExecute()
{
base.PostExecute();
}
public string sourceFile = Dts.Variables["User::FileName"].Value.ToString();
public override void CreateNewOutputRows()
{
foreach (var myXmlData in (
from elements in StreamReader(sourceFile, "INSTRUMENT")
select new
{
PORTF_LIST = elements.Element("PORTF_LIST").Value,
PRT_FULL_NAME = elements.Element("PRT_FULL_NAME").Value,
ISIN = elements.Element("ISIN").Value
}
))
{
try
{
Output0Buffer.AddRow();
Output0Buffer.PORTF_LIST = myXmlData.PORTF_LIST;
Output0Buffer.PRT_FULL_NAME = myXmlData.PRT_FULL_NAME;
Output0Buffer.ISIN = myXmlData.ISIN;
}
catch (Exception e)
{
string errorMessage = string.Format("Data retrieval failed: {0}", e.Message);
bool cancel;
ComponentMetaData.FireError(0, ComponentMetaData.Name, errorMessage,string.Empty,0, out cancel);
}
}
}
public static IEnumerable<XElement> StreamReader(String filename, string elementName)
{
// Create an XML reader for this file.
using (XmlReader reader = XmlReader.Create(filename))
{
reader.MoveToContent(); // will not advance reader if already on a content node; if successful, ReadState is Interactive
reader.Read(); // this is needed, even with MoveToContent and ReadState.Interactive
while(!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if(reader.NodeType == XmlNodeType.Element && reader.Name.Equals(elementName))
{
// this advances the reader...so it's either XNode.ReadFrom() or reader.Read(), but not both
var matchedElement = XNode.ReadFrom(reader) as XElement;
if(matchedElement != null)
yield return matchedElement;
}
else
reader.Read();
}
reader.Close();
}
}
}
]]>
</File>
</Files>
<ReadOnlyVariables>
<Variable Namespace="User" DataType="String" VariableName="FileName" />
</ReadOnlyVariables>
<ReadWriteVariables>
</ReadWriteVariables>
</ScriptComponentProject>
I've checked the code in a console app and it reads the XML file fine, but no luck with the BIML. There are about 250 odd columns so I am trying to avoid doing this manually so if you have any ideas what I am doing wrong I'd really appreciate it!
It seems that the script task does not like underscores in the OutputBuffer.
I created a stub package manually and intellisense had PORTFLIST rather than PORTF_LIST when assigning the value.
So that snippit of code should be:
Output0Buffer.AddRow();
Output0Buffer.PORTFLIST = myXmlData.PORTF_LIST;
Output0Buffer.PRTFULLNAME = myXmlData.PRT_FULL_NAME;
Output0Buffer.ISIN = myXmlData.ISIN
I have another error, my favorite "EmitSsis. Internal Compiler Error: Workflow EmitSsis contains fatal errors.", but at least this one is solved!
Thanks Bill for your help, and sorry I led you down the garden path with the wrong column name in the posted error, or you probably would have known the issue!

ASP.Net MVC with MySql - unable to find the requested .Net Framework Data Provider

I have installed the MySql Connector 6.6.5 from http://dev.mysql.com/downloads/connector/net/
I have added it as a reference in my asp.net MVC website (please see screenshots below), and changed "Copy local" to true.
However, when I get to the line:var calls = db.Calls.ToList(); I get the error:
Unable to find the requested .Net Framework Data Provider. It may not be installed.
I've included my code below. Can anyone please let me know what I'm missing?
Thanks, Mark
Controller Call.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MySql.Data;
using MySql.Data.Entity;
using MySql.Data.MySqlClient;
using MySql.Web;
using System.Data;
namespace bm.Controllers
{
public class CallController : Controller
{
private CallContext db = new CallContext();
//
// GET: /Calls/
public ActionResult Index()
{
var calls = db.Calls.ToList();
return View(calls);
}
}
}
CallContext.cs
using bm.Models;
using System.Data.Entity;
public class CallContext : DbContext
{
static CallContext()
{
}
public CallContext()
: base("callsConn")
{
}
public DbSet<Call> Calls { get; set; }
}
Web.Config Connection String:
<connectionStrings>
<add name="callsConn" connectionString="Server=xxx;Port=3306;Database=xxxx;Uid=root;Pwd=;" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
References:
Update
I seem to have solved this by adding the following to my web.config:
<system.data>
<DbProviderFactories >
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.6.5.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
Hope it helps someone else.
Mark

ASP.NET MVC 4 EF5 with MySQL

So I've just picked up VS2012 and I want to start an ASP.NET MVC 4 app with EF5.
My host does not have MSSQL so I have to use MySQL.
How do I tell my app that it should use MySQL? (I either want to use the devart MySQL connector or the one from mysql.com)
You need to setup your config with a connection string, DbProviderFactory and a custom DatabaseInitializer for MySql Connector 6.5.4. I have detailed the full step for getting EF5 and MySql to play, including code for the initializers on my blog. If you require ASP.Net membership provider solution it been asked before: ASP.NET Membership/Role providers for MySQL? I will post the solution here also for a complete EF5 MySql solution.
The MySql connector does not currently support EF 5 migration and ASP.NET only supports SimpleMembership (MVC4 default) on MS SQL not MySql. The solution below is for Code First.
The steps are:
Grab EF 5 from NuGet
Grab MySql.Data and MySql.Data.Entity from NuGet (6.5.4) or MySql (6.6.4)
Configure a MySql Data Provider
Configure a MySql Connection String
Create a Custom MySql Database Initializer
Configure the Custom MySql Database Initializer
Configure ASP.NET membership if you require it
DbProvider
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient"/>
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient"
description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data" />
</DbProviderFactories>
</system.data>
Connection String
<connectionStrings>
<add name="ConnectionStringName"
connectionString="Datasource=hostname;Database=schema_name;uid=username;pwd=Pa$$w0rd;"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
Database Initializer
If you are using MySql connector from NuGet (6.5.4) then a custom initializer is required. Code available at http://brice-lambson.blogspot.se/2012/05/using-entity-framework-code-first-with.html
or at http://www.nsilverbullet.net/2012/11/07/6-steps-to-get-entity-framework-5-working-with-mysql-5-5/
Then add this to configuration
<configSections>
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,
EntityFramework, Version=5.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</configSections>
<entityFramework>
<contexts>
<context type="Namespace.YourContextName, AssemblyName">
<databaseInitializer
type="Namespace.YourChosenInitializer, AssemblyName">
</databaseInitializer>
</context>
</contexts>
<defaultConnectionFactory
type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data" />
</entityFramework>
ASP.NET Membership
<membership defaultProvider="MySqlMembershipProvider">
<providers>
<clear />
<add name="MySqlMembershipProvider"
type="MySql.Web.Security.MySQLMembershipProvider,
MySql.Web, Version=6.5.4.0, PublicKeyToken=c5687fc88969c44d"
autogenerateschema="true"
connectionStringName="*NAME_OF_YOUR_CONN_STRING*"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="false"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
applicationName="/" />
</providers>
</membership>
Get the AccountController and Views working:
Delete the MVC 4 AccountController, AccountModels, Account view folder and _LoginPartial shared view
Create a new MVC 3 web application
Copy the MVC 3 AccountController, AccountModels, Account view folder and _LogOnPartial shared view into your MVC 4 application
Replace #Html.Partial(“_LoginPartial”) in the shared _Layout view with #Html.Partial(“_LogOnPartial”)
<add name="ConnectionString" providerName="MySql.Data.MySqlClient" connectionString="Data Source=127.0.0.1; port=3306; Initial Catalog=DbName; uid=root; pwd=*Password*;" />
Install Package:
PM> Install-Package EntityFramework
PM> Update-Package EntityFramework
PM> Install-Package MySql.Data.Entity
Web.config
<connectionStrings>
<add name="DefaultConnection"
providerName="MySql.Data.MySqlClient"
connectionString="Data Source=localhost;port=3306;Initial Catalog=api_db;User Id=root;password=''"/>
</connectionStrings>
Create Model Class
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace LiteRemit.Models
{
[Table("customers")]
public class CustomerModel
{
[Key]
public int CustomerId { get; set; }
public string Name { get; set; }
public string Country { get; set; }
}
}
Create Model Context:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace LiteRemit.Models
{
public class MySqlCon : DbContext
{
//MySql Database connection String
public MySqlCon() : base(nameOrConnectionString: "DefaultConnection") { }
public virtual DbSet<CustomerModel> Customers { get; set; }
}
}
Create Controller Class
using LiteRemit.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace LiteRemit.Controllers
{
public class HomeController : Controller
{
MySqlCon _con;
public HomeController()
{
_con = new MySqlCon();
}
public ActionResult Index()
{
return View(_con.Customers.ToList());
}
}
}
Code add view page:
#using LiteRemit.Models
#model IEnumerable<CustomerModel>
<table border="1">
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.CustomerId)</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>#Html.DisplayFor(modelItem => item.Country)</td>
</tr>
}
</table>

Entity Framework 4.1 Database First does not add a primary key to the DbContext T4 generated class

I am just getting started with Entity Framework 4.1, trying out the "database first" mode. When EF generates a Model class with the "ADO.Net DbContext Generator," shouldn't it identify the primary key for the class with a [Key] attribute? Without this, it appears incompatible with the T4 MVCScaffolding.
Here are the details:
Using the Entity Data Model Designer GUI, I have added a simple "country" table to the model from my existing database. The GUI correctly identifies a single integer identity key field named "PK" as my primary key. (Alas! I'm a new user so I can't add a screenshot. I've included the CSDL instead below.) However, when EF generates code using the "ADO.Net DbContext Generator", it does not identify the PK field as the key field in the generated class (see code excerpt below).
The CSDL for the "country" table:
<edmx:ConceptualModels>
<Schema Namespace="EpiDataModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
<EntityContainer Name="EpiModelEntities" annotation:LazyLoadingEnabled="true">
<EntitySet Name="countries" EntityType="EpiDataModel.country" />
</EntityContainer>
<EntityType Name="country">
<Key>
<PropertyRef Name="PK" />
</Key>
<Property Name="PK" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Name="Abbreviation" Type="String" Nullable="false" MaxLength="200" Unicode="false" FixedLength="false" />
<Property Name="Name" Type="String" MaxLength="1024" Unicode="false" FixedLength="false" />
<Property Name="Description" Type="String" MaxLength="1024" Unicode="false" FixedLength="false" />
<Property Name="Sequence" Type="Int32" />
</EntityType>
</Schema>
</edmx:ConceptualModels>
Here's the autogenerated code:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace MvcApplication1.Areas.Epi.Models
{
public partial class country
{
public int PK { get; set; }
public string Abbreviation { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Sequence { get; set; }
}
}
This causes a problem when I try to scaffold a controller using the MVCScaffolding T4 template. I get an error "No properties appear to be primary keys." The command and output from the NuGet Package Manager Console is below:
PM> scaffold controller MvcApplication1.Areas.Epi.Models.country -Area Epi -NoChildItems -DbContextType MvcApplication1.Areas.Epi.Models.EpiModelEntities -Force
Scaffolding countriesController...
Get-PrimaryKey : Cannot find primary key property for type 'MvcApplication1.Areas.Epi.Models.country'. No properties appear to be primary keys.
At C:\work\EPI\EPIC_MVC3\sandbox\MvcApplication1\packages\MvcScaffolding.1.0.6\tools\Controller\MvcScaffolding.Controller.ps1:74 char:29
+ $primaryKey = Get-PrimaryKey <<<< $foundModelType.FullName -Project $Project -ErrorIfNotFound
+ CategoryInfo : NotSpecified: (:) [Get-PrimaryKey], Exception
+ FullyQualifiedErrorId : T4Scaffolding.Cmdlets.GetPrimaryKeyCmdlet
However, if I manually change the generated class to add a [Key] attribute to the field, then the exact same scaffolding command shown above works fine:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; // manually added
namespace MvcApplication1.Areas.Epi.Models
{
public partial class country
{
[Key] // manually added
public int PK { get; set; }
public string Abbreviation { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Sequence { get; set; }
}
}
So why aren't EF Database First and the T4 MVCScaffolding playing nice together? And even without the scaffolding issue, don't the EF classes need to know what the key field(s) are?
T4 Templates doesn't use data annotations because classes generated from templates don't need them. EF also don't need them because mapping is defined in XML files not in code. If you need data annotations you must either:
Modify T4 template to use them (this requires understanding of EF metadata model)
Don't use templates and use code first instead
Use buddy classes to manually add data annotations and hope that scaffolding will recognize them
If someone does want to do this, I found some good interesting templates on
james mannings github
Those templates have more functionality, but the bit I pulled out of these was:
1) Replace the usings at the top of Entity.tt with
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
<#
if (efHost.EntityFrameworkVersion >= new Version(4, 4))
{
WriteLine("using System.ComponentModel.DataAnnotations.Schema;");
}
#>
2) Then find this line (that prints out the properties)
<#= Accessibility.ForProperty(property) #> <#= typeUsage #> <#= code.Escape(property) #> { get; set; }
3) and prepend this template code
var attributes = new List<string>();
var isPartOfPrimaryKey = efHost.EntityType.KeyMembers.Contains(property);
var primaryKeyHasMultipleColumns = efHost.EntityType.KeyMembers.Count > 1;
if (isPartOfPrimaryKey)
{
if (primaryKeyHasMultipleColumns)
{
var columnNumber = efHost.EntityType.KeyMembers.IndexOf(property);
attributes.Add(String.Format("[Key, Column(Order = {0})]", columnNumber));
}
else
{
attributes.Add("[Key]");
}
}
PushIndent(new string(' ', 8));
foreach (var attribute in attributes)
{
WriteLine(attribute);
}
ClearIndent();