MVC4 + Entity Framework 4.4 + MySql + POCO/Code First
I'm setting up the above configuration .. here are my classes:
namespace BTD.DataContext
{
public class BTDContext : DbContext
{
public BTDContext()
: base("name=BTDContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Conventions.Remove<System.Data.Entity.Infrastructure.IncludeMetadataConvention>();
}
public DbSet<Product> Products { get; set; }
public DbSet<ProductImage> ProductImages { get; set; }
}
}
namespace BTD.Data
{
[Table("Product")]
public class Product
{
[Key]
public long ProductId { get; set; }
[DisplayName("Manufacturer")]
public int? ManufacturerId { get; set; }
[Required]
[StringLength(150)]
public string Name { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required]
[StringLength(120)]
public string URL { get; set; }
[Required]
[StringLength(75)]
[DisplayName("Meta Title")]
public string MetaTitle { get; set; }
[DataType(DataType.MultilineText)]
[DisplayName("Meta Description")]
public string MetaDescription { get; set; }
[Required]
[StringLength(25)]
public string Status { get; set; }
[DisplayName("Create Date/Time")]
public DateTime CreateDateTime { get; set; }
[DisplayName("Edit Date/Time")]
public DateTime EditDateTime { get; set; }
}
[Table("ProductImage")]
public class ProductImage
{
[Key]
public long ProductImageId { get; set; }
public long ProductId { get; set; }
public long? ProductVariantId { get; set; }
[Required]
public byte[] Image { get; set; }
public bool PrimaryImage { get; set; }
public DateTime CreateDateTime { get; set; }
public DateTime EditDateTime { get; set; }
}
}
Here is my web.config setup...
<connectionStrings>
<add name="BTDContext" connectionString="Server=localhost;Port=3306;Database=btd;User Id=root;Password=mypassword;" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
The database AND tables already exist...
I'm still pretty new with mvc but was using this tutorial
The application builds fine... however when I try to add a controller using Product (BTD.Data) as my model class and BTDContext (BTD.DataContext) as my data context class I receive the following error:
Unable to retrieve metadata for BTD.Data.Product using the same
DbCompiledModel to create context against different types of database
servers is not supported. Instead, create a separate DbCompiledModel
for each type of server being used.
I am at a complete loss - I've scoured google with almost every different variation of that error message above I can think of but to no avail.
Here are the things i can verify...
MySql is working properly
I'm using MySql Connector version 6.5.4 and have created other ASP.net web forms + entity framework applications with ZERO problems
I have also tried including/removing this in my web.config:
<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, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
I've literally been working on this bug for days - I'm to the point now that I would be willing to pay someone to solve it.. no joke... I'd really love to use MVC 4 and Razor - I was so excited to get started on this, but now i'm pretty discouraged - I truly appreciate any help/guidance on this!
Also note - i'm using Entityframework from Nuget...
Another Note
I was using the default visual studio template that creates your MVC project with the account pages and other stuff. I JUST removed all references to the added files because they were trying to use the "DefaultConnection" which didn't exist - so i thought those files may be what was causing the error - however still no luck after removing them -
I just wanted to let everyone know i'm using the visual studio MVC project template which pre-creates a bunch of files. I will be trying to recreate this all from a blank MVC project which doesn't have those files - i will update this once i test that
UPDATE TO USING VS MVC Basic Template: Same error resulted - still no solution
ANOTHER PERSON EXPERIENCING THE SAME PROBLEM
Right here is another stackoverflow question that mimics mine - however I tried his solution to no avail - maybe someone else who is having this same problem can benefit from the link
UPDATE
I recently just tried putting this into MS Sql Server and the view scaffolding adds fine with no error - so I'm not sure if its my MySql database or connection string or what... driving me nuts..
Other References
It appears someone else is having the same issues I am - the only difference is they are using sql server - I tried tweaking all my code to follow the suggestions on this stackoverflow question/answer here but still to no avail
POSSIBLE FIX???
So this is weird... after hooking it up to MS Sql Server and adding the controller, then reverting the connection string to MySql it is actually WORKING with MySql... what the heck!??
So it seems that when you try to add your controller and the view scaffolding (is that the right phrase?) is added WITH the mysql connection string it fails...however if you hook it up to a sql server db, generate the scaffolding/controller, then revert to mysql connection string it works.... ?!?!
It seems that MVC4 Controller scaffolding is not properly recognizing MySql Connection String. Change the connection string as shown below when generating EF CRUD code for Controllers:
<connectionStrings>
<add name="BTDContext" connectionString="Data Source=host_name;Database=database_name;uid=user_id;pwd=password;" providerName="System.Data.SqlClient" />
</connectionStrings>
Change it back to standard when running the application:
<connectionStrings>
<add name="BTDContext" connectionString="Data Source=host_name;Database=database_name;uid=user_id;pwd=password;" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
Note the change, provider name.
The imesh suggesting almost solve my problem, but additionally I temporary commented line
[DbConfigurationType(typeof(MySqlEFConfiguration))]
which was in DBContext class. And of course after creation controller this line should be uncommented and change back System.Data.SqlClient to MySql.Data.MySqlClient in config file.
Using VS 2013, MySQL Connector/NET 6.9.6, Entity Framework 6, Web API 2.2 and MySQL server 5.7 I had to combine the answers to prevent the error about "Unable to retrieve metadata".
To successfully add a controller to a .NET project that uses a MySQL connection, do the following:
Temporarily add a connection string with System.Data.SqlClient as the providerName, and comment the one for MySQL. It doesn't matter whether the connection string is valid.
Ensure that MySqlEFConfiguration isn't enabled in any way.
Rebuild.
About the second point, the MySQL documentation on using Connector/NET with EF6 states three possible ways to enable the MySqlEFConfiguration. Ensure that none of these are enabled while adding controllers using the VS template.
Adding the DbConfigurationTypeAttribute on the context class:
[DbConfigurationType(typeof(MySqlEFConfiguration))]
Calling DbConfiguration.SetConfiguration(new MySqlEFConfiguration())
at the application startup
Set the DbConfiguration type in the configuration file:
<entityFramework
codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration,
MySql.Data.Entity.EF6">
I tested also around this bug and saw an other problem.
Following code is in my Web.config (without, it don't work):
<entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.Entity.EF6">
Changed it to:
<entityFramework>
Works for me... add the scaffolding and then change it back
I've been having the same problem using EF 4.3.1 and MySql Connecter/Net 6.6.4.0
This worked for me, no need to connect to a different db or extra code in the Context class.
Add this between the entityFramework tags in your web.config file when you want to build a scaffold:
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=.\SQLEXPRESS; Integrated Security=True; MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>
Then comment the above code out when you want to run migrations and vice versa.
So you web.config will look like so:
<entityFramework>
<contexts>
<context type="ApptManager.Models.AppointmentsManagerContext, ApptManager">
<databaseInitializer type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[ApptManager.Models.AppointmentsManagerContext, ApptManager], [ApptManager.Migrations.Configuration, ApptManager]], EntityFramework" />
</context>
</contexts>
<!--<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=.\SQLEXPRESS; Integrated Security=True; MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>-->
</entityFramework>
This is quite ridiculous how .NET developers have to jump through some arduous hoops in order to get their code working.
Working example
https://github.com/dublinan/mvc-mysql-ef-example
Links to my github project
Please try using the
System.ComponentModel.DataAnnotations
namespace along with the [Key] attribute on the EDM class members.
It worked for me.
Related
When I render my template without the EditHistory member, this works. However, when I add that additional member that is within my application I get an exception Could not load file or assembly 'Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. Models is the project containing ContentModel, EditHistory and UserDetail.
public class ContentModel
{
public string Html { get; set; }
public string Title { get; set; }
public EditHistory History { get; set; }
}
public class EditHistory
{
public IReadOnlyCollection<UserDetail> Authors { get; set; }
}
public class UserDetail
{
public string Name { get; set; }
public string EmailAddress { get; set; }
}
I am wrapping ContentModel in a RazorDynamicObject as such:
Razor.Run("default.cshtml", typeof(ContentModel), RazorDynamicObject.Create(cm));
As mentioned above, it works without EditHistory being present, but fails when it is.
The sandbox is set up verbatim as per how it's done at https://antaris.github.io/RazorEngine/Isolation.html
How do I get it to work with complex custom types?
Running under ASP.NET.
Edit
I have created a minimal reproduction of the issue I'm facing. It's at https://github.com/knightmeister/RazorEngineIssue. If package restore fails, manually install-package razorengine.
First of all; I was never able to get your GitHub-code running. The following is based on my own reproducing code.
I think that you're getting Could not load file or assembly-exceptions because when you setup the sandbox AppDomain you're setting:
adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
This won't work in ASP.NET because assemblies are in the bin subfolder. To fix that, simply do this instead:
adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
+ "\\bin";
However, ASP.NET will by default shadow copy assemblies. Therefore just doing this change will probably cause another exception:
ArgumentException: Object type cannot be converted to target type.
That's because there's a mixup between assemblies loaded in the default app domain and the sandbox. The ones in the default app domain are located in a temporary shadow copy location and the ones in the sandbox are located in the bin-folder of your web application root.
The easiest way to fix this is to disable shadow copying by adding the following line under <system.web> in your Web.config:
<hostingEnvironment shadowCopyBinAssemblies="false"/>
In addition; I think it's better and easier to skip using RazorDynamicObject and instead mark your models with [Serializable]. In fact I never got RazorDynamicObject working properly.
The rest of this answer summarizes what I did to come to this conclusion
I think that this is due to a bug or limitation in RazorEngine. (I'm not so sure about this anymore, it might very well be that shadow copying and RazorDynamicObject cannot work together)
I've spent a couple of hours trying to figure out how to get this working but I've always ended up with a security exception being thrown from RazorEngine.
There is, however, a possible workaround: Ditch RazorDynamicObject and mark your model classes as serializable.
[Serializable]
public class ContentModel
{
public string Html { get; set; }
public string Title { get; set; }
public EditHistory History { get; set; }
}
[Serializable]
public class EditHistory
{
public IReadOnlyCollection<UserDetail> Authors { get; set; }
}
[Serializable]
public class UserDetail
{
public string Name { get; set; }
public string EmailAddress { get; set; }
}
And do:
Razor.Run("default.cshtml", typeof(ContentModel), cm); // no RazorDynamicObject
I couldn't get your repro code running, so I created my own based on your code:
Create a new Console application (Visual Studio)
In the package manager console, run: install-package razorengine
Copy code from your repro:
Line 25-38 and 43-65 from:
https://github.com/knightmeister/RazorEngineIssue/blob/master/Global.asax.cs
All models from: https://github.com/knightmeister/RazorEngineIssue/blob/master/Models/Models.cs
Mark models with [Serializable].
Remove RazorDynamicObject
To ensure that we really can render user details from the authors list, change the test template to:
string template = "#Model.History.Authors[0].EmailAddress";
Also, to make that template work, change Authors in EditHistory from IReadOnlyCollection<> to IReadOnlyList<>
I created a GIST with the resulting code:
https://gist.github.com/mwikstrom/983c8f61eb10ff1e915a
This works for me. It prints hello#world.com just as it should.
ASP.NET will shadow copy assemblies by default and that will cause additional problems with sandboxing.
To get this working under ASP.NET you'll have to do the following changes:
Disable ASP.NET shadow copying by adding the following under <system.web> in your Web.config file:
<hostingEnvironment shadowCopyBinAssemblies="false"/>
Append \bin to the sandbox's application base path. So in createRazorSandbox(...) do:
adSetup.ApplicationBase =
AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "\\bin";
I have tested this and it works just fine. My test project is simply:
An empty ASP.NET Web Application (created with Visual Studio), with install-package razorengine
<hostingEnvironment shadowCopyBinAssemblies="false"/> in Web.config.
The following Global.asax.cs:
https://gist.github.com/mwikstrom/ea2b90fd0d306ba3498c
There are other alternatives (besides disabling shadow copying) listed here:
https://github.com/Antaris/RazorEngine/issues/224
I mostly don't use complex types but a general rule is usually that only primitive datatypes are transferred ok (my own rule, since values often get lost for me otherwise). However, when looking at some old source code I noticed I did use many complex types, but I populated them in the Controller (e.g. in Public ActionResult Index()). After some reading I think it might work if you use something similar to this (untested, MSDN source, 2nd source):
[MetadataType(typeof(EditHistory))]
public partial class ContentModel
{
public string Html { get; set; }
public string Title { get; set; }
public EditHistory History { get; set; }
}
[MetadataType(typeof(UserDetail))]
public partial class EditHistory
{
public IReadOnlyCollection<UserDetail> Authors { get; set; }
}
public class UserDetail
{
public string Name { get; set; }
public string EmailAddress { get; set; }
}
Well I was developing an ASP.NET webforms app in Visual Studio and it worked well. It has 2 SQL Server connection strings, one for ASP.NET Identity and the other for my own tables. Then I wanted to test my app on a real server. So I changed the connection strings to this:
<add name="DefaultConnection"
connectionString="Data Source=WIN-9I87AF3QUO9;Initial Catalog=aspnet-AdManager-20141230074246;Integrated Security=True"
providerName="System.Data.SqlClient" />
<add name="StracturesConnection"
connectionString="Data Source=WIN-9I87AF3QUO9;Initial Catalog=aspnet-AdManager-20141230074246;Integrated Security=True"
providerName="System.Data.SqlClient" />
Now the DefaultConnection works well. but the second one doesn't.
I get an error:
A network-realated or instance-specific error
here is the code of my DbContext
public class StructureDbContext : DbContext
{
public StructureDbContext()
: base("StructuresConnection")
{
}
public DbSet<Structure> Structures { get; set; }
public DbSet<StructureType> StructureTypes { get; set; }
public DbSet<Reservation> Reservations { get; set; }
}
and this one is the db initializer:
public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<StructureDbContext>
{
protected override void Seed(StructureDbContext context)
{
Structure s = new Structure();
s.StructureTypeID = null;
s.Description = "Test";
s.CityID = 45;
s.Address = "test";
s.Price = 400;
context.Structures.Add(s);
StructureType t = new StructureType();
t.Name = "بیلبورد";
context.StructureTypes.Add(t);
}
}
}
Well the problem is a miss spelling. in the connection string I wrote "Stractures" and this is wrong! sorry for wasting your time. I should go and check why this works on visual studio!
I've been doing this Movies database tutorial for MVC3 with Code First, but when I try to access the /Movies page I get the error
"The network path was not found".
I don't think I missed any steps in the tutorial.
I created my Movie model
namespace Movies.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseData { get; set; }
public string Genre { get; set; }
public Decimal Price { get; set; }
}
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
}
I added the connection string as well
<add name="MovieDBContext" connectionString="Data Source=|DataDirectory|Movies.sdf" providerName="System.Data.SqlClient"/>
In the tutorial the connection string is this, but it was giving me errors trying to create the controller so I found somewhere to change it to the one above, which allows me to create the controller fine but then get the network path error.
<add name="MovieDBContext" connectionString="Data Source=|DataDirectory|Movies.sdf" providerName="System.Data.SqlServerCe.4.0"/>
Did you try?
new DbContext("MovieDBContext")
http://msdn.microsoft.com/en-us/library/gg679467(v=vs.103).aspx
Did you try to emit at least a select 1; from your code?
<system.data>
<DbProviderFactories>
<add name="SQL Server Compact Edition Data Provider"
invariant="System.Data.SqlServerCe"
description=".NET Framework Data Provider for Microsoft SQL Server Compact Edition"
type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"/>
</DbProviderFactories>
See also Entity Framework on SQL Server CE without driver install
i think you have forgot to add
Integrated Security=SSPI
I've been struggling to try and get Data Services to work with the new LightSwitch 2.0 OData Data Source.
Noticing that OData 3.0 is still not supported I had to fall back to 2.0 version.
It happens that my Data Context is nothing but wrapper over some xml serialized object to enable OData access.
This "magic" happens using the Reflection Provider and it works fine regarding all the CRUD operations.
The problems start when I try to use this service in LightSwitch, and realize that all my entity associations are wrong.
The situation I have is exactly the same as if you look at Microsoft's sample code.
So, using this data model:
[DataServiceKeyAttribute("OrderId")]
public class Order
{
public int OrderId { get; set; }
public string Customer { get; set; }
public IList<Item> Items { get; set; }
}
[DataServiceKeyAttribute("Product")]
public class Item
{
public string Product { get; set; }
public int Quantity { get; set; }
}
It's obvious the "one-to-many" relationship between Order [1 - *] Items.
But looking at the xml metadata of this service, the association is declared as "many-to-many":
<Association Name="Order_Items">
<End Type="WEBfactory.StreamInsight.Adapters.Carel.DataServices.Order" Multiplicity="*" Role="Order"/>
<End Type="WEBfactory.StreamInsight.Adapters.Carel.DataServices.Item" Multiplicity="*" Role="Items"/>
</Association>
Now, this doesn't really bother much when using a "Service Reference" client, but since LightSwitch doesn't support "many-to-many" relationships I always get a warning when trying consume this service and the relations are neither imported, nor possible to manually define.
Does anyone have a clue how to work enforce a relationship type using the Reflection Provider?
Thanks!!
The relationship between Order and Item in this case actually is many:many - if it were 1:many as you propose, an Item could only be in one Order.
That said, you can create the 1:* relationship by adding a corresponding property to the Item class:
[DataServiceKeyAttribute("Product")]
public class Item
{
public string Product { get; set; }
public int Quantity { get; set; }
public Order Order { get; set; }
}
That results in the following $metadata, which may or may not cause the same problem:
<Association Name="Order_Items">
<End Type="Scratch.Web.Order" Multiplicity="*" Role="Order"/>
<End Type="Scratch.Web.Item" Multiplicity="*" Role="Items"/>
</Association>
<Association Name="Item_Order">
<End Type="Scratch.Web.Order" Multiplicity="0..1" Role="Order"/>
<End Type="Scratch.Web.Item" Multiplicity="*" Role="Item"/>
</Association>
This is likely a limitation of the Reflection provider (I'll edit this answer if it turns out not to be), so the only workarounds today are to use either the EF provider or a custom provider.
I would make my WCF RIA Service work as described here:
How to Create a Many-to-Many Relationship
http://blogs.msdn.com/b/lightswitch/archive/2010/12/16/how-to-create-a-many-to-many-relationship-andy-kung.aspx
I have created a new project from template. I used windows phone cloud toolkit template which server side is a regular asp.net mvc3 project with EF 4.1 code first.
My datacontext is called SqlDataContext and has the following constructor:
public SqlDataContext()
: base(ConfigReader.GetConfigValue("SqlSampleDataContextConnectionString"))
{
}
public DbSet<SqlSampleData> SqlSampleData { get; set; }
My connection string defined in web.config as:
<connectionStrings>
<add name="ApplicationServices" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=true;User Instance=true;AttachDBFilename=|DataDirectory|\WPCloudApp26.mdf;Initial Catalog=WPCloudApp26;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>
I have added a new model called Report and now i want to create new controller with the add controller wizard, using my new Report model and the SqlDataContext. when I try to create i get the following error:
Unable to retrieve metadata for 'WPCloudApp1.Web.Models.Report'.
The 'SqlSampleDataContextConnectionString' setting is not available.
Parameter name: key
And it fails to create the new controller.
What am i doing wrong ? My background in this topic very scarce so please try to make it as simple as possible.
My Report class:
namespace WPCloudApp26.Web.Models
{
using System;
using System.ComponentModel.DataAnnotations;
// Summary:
// Sample Entity Framework 4.1 data class for SQL Azure.
// Using EF 4.1 Code-First, the database structure will be created to mirror this class properties.
// For more information, visit the ADO.NET Entity Framework website at http://msdn.microsoft.com/data/aa937723
public class Report
{
[Key]
public int Id { get; set; }
public string UserId { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public bool IsPublic { get; set; }
}
}
Thanks.
Edit:
I have no problem running the application, only adding a new controller.
Try to replace :
<connectionStrings>
<add name="ApplicationServices" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=true;User Instance=true;AttachDBFilename=|DataDirectory|\WPCloudApp26.mdf;Initial Catalog=WPCloudApp26;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>
By:
<connectionStrings>
<add name="SqlDataContext" connectionString="Data Source=.\SQLEXPRESS;Integrated Security=true;User Instance=true;AttachDBFilename=|DataDirectory|\WPCloudApp26.mdf;Initial Catalog=WPCloudApp26;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>
The context should be the value stored in the web.config