What happened to HasColumnName for EF Core? - c#

So I am trying to map an internal property to my database and according to this article online this is how you are supposed to do it. The other resources that I found also tell me to do the same thing. For whatever reason the method doesn't exist and I can't find online what they renamed it too or if they just removed the method.
Here is my code:
public class Criteria : DbEntity
{
internal string _Condition { get; set; }
[NotMapped]
public Condition Condition
{
get
{
return string.IsNullOrEmpty(_Condition) ? null : JsonConvert.DeserializeObject<Condition>(_Condition);
}
set
{
_Condition = JsonConvert.SerializeObject(value);
}
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Criteria>().Property(b => b._Condition);//.HasColumnName("Condition"); <-- this doesn't exist...
}

Had to install Microsoft.EntityFrameworkCore.Relational to fix the issue.
Edit:
Credit goes to Ivan Stoev for finding this out

If you're using SQL Server and using Core 3.1x, you can also fix this by installing Microsoft.EntityFrameworkCore.SqlServer.

Related

How do I implement a simple "complex type" in Entity Framework Core 2/C#?

I'm using Entity Framework and .Net Core 2.0 for the first time (I'm also pretty new to C#, but I've been using the traditional .Net Framework & VB since version 1... so I'm no newbie to .Net development), and I've already run into a problem creating my database.
Take this simple scenario: I want to store some information about some electric pumps. Two of the properties are a min/max type range, so I've implemented these as a simple class, thus:
public class Pump
{
[Key]
public int pumpId { get; set; }
public string pumpName { get; set; }
public int pumpControlChannel { get; set; }
public MinMax normalCurrent { get; set; }
public MinMax normalFlowRate { get; set; }
}
[ComplexType]
public class MinMax
{
public int min { get; set; }
public int max { get; set; }
}
As you can see, I've tried the [ComplexType] decorator, to no avail.
Anyway, now create a dead simple DBContext class to manage my Pumps class. I'm using Sqlite:
public class EFDB : DbContext
{
public DbSet<Pump> pumps { get; private set; }
private static DbContextOptions GetOptions(string connectionString)
{
var modelBuilder = new DbContextOptionsBuilder();
return modelBuilder.UseSqlite(connectionString).Options;
}
public EFDB(string connectionString) : base(GetOptions(connectionString)) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
try
{
// modelBuilder.ComplexType<MinMax>(); // ComplexType not recognised
base.OnModelCreating(modelBuilder);
}
catch (Exception ex)
{
System.Diagnostics.Debugger.Break();
}
}
}
and lastly a simple static class to call it (I embeded it in a bigger program... to duplicate this problem you could just stick the code lines into program.cs):
public static class TryMe
{
public static void MakeMeFail()
{
using (var db = new EFDB("FileName=C:\\temp\\test_effail.db"))
{
try
{
db.Database.EnsureCreated();
}
catch (Exception ex)
{
System.Diagnostics.Debugger.Break(); // If we hit this line, it fell over
}
}
System.Diagnostics.Debugger.Break(); // If we hit this line, it worked.
}
}
Just call TryMe.MakeMeFail(), the code fails at db.Database.EnsureCreated().
From everything I've read, [ComplexType] should do what I want... but it Just Doesn't. Nor can I find modelBuilder.ComplexType<T> anywhere.
It may just be a library reference I'm missing...? The above code uses the following:
using System;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
However, NONE of the documentation/examples I can find anywhere show which libraries need referencing!
Thanks in advance.
[PS: Apologies to those who already saw this question, I'm using EF Core 2.0, NOT EF6]
Typical... it's always the way, isn't it? 5 minutes after posting, you discover the answer to your own question....
The answer, in this case, can be found here:
https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities
EF Core calls this sort of entity an "owned" entity, rather than a "complex type".
Simply adding these lines to `OnModelCreating' fixed the issue:
modelBuilder.Entity<Pump>().OwnsOne(p => p.normalCurrent);
modelBuilder.Entity<Pump>().OwnsOne(p => p.normalFlowRate);
The database now creates (correctly, I think, I haven't verified that yet).

Returning a List or IEnumerable with IHttpActionResult [duplicate]

I am working with ASP.NET MVC 5 Web Api. I want consult all my users.
I wrote api/users and I receive this:
"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'"
In WebApiConfig, already I added these lines:
HttpConfiguration config = new HttpConfiguration();
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
But it still doesn't work.
My function for return data is this:
public IEnumerable<User> GetAll()
{
using (Database db = new Database())
{
return db.Users.ToList();
}
}
If you are working with EF, besides adding the code below on Global.asax
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Dont`t forget to import
using System.Data.Entity;
Then you can return your own EF Models
Simple as that!
When it comes to returning data back to the consumer from Web Api (or any other web service for that matter), I highly recommend not passing back entities that come from a database. It is much more reliable and maintainable to use Models in which you have control of what the data looks like and not the database. That way you don't have to mess around with the formatters so much in the WebApiConfig. You can just create a UserModel that has child Models as properties and get rid of the reference loops in the return objects. That makes the serializer much happier.
Also, it isn't necessary to remove formatters or supported media types typically if you are just specifying the "Accepts" header in the request. Playing around with that stuff can sometimes make things more confusing.
Example:
public class UserModel {
public string Name {get;set;}
public string Age {get;set;}
// Other properties here that do not reference another UserModel class.
}
Given right answer is one way to go, however it is an overkill when you can fix it by one config settings.
Better to use it in the dbcontext constructor
public DbContext() // dbcontext constructor
: base("name=ConnectionStringNameFromWebConfig")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
Asp.Net Web API Error: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'
Add this code to global.asax below on Application_Start:
Update from .Ignore to .Serialize . It must work.
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
public class UserController : ApiController
{
Database db = new Database();
// construction
public UserController()
{
// Add the following code
// problem will be solved
db.Configuration.ProxyCreationEnabled = false;
}
public IEnumerable<User> GetAll()
{
return db.Users.ToList();
}
}
I resolved it using this code to WebApiConfig.cs file
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
I don't like this code:
foreach(var user in db.Users)
As an alternative, one might do something like this, which worked for me:
var listOfUsers = db.Users.Select(r => new UserModel
{
userModel.FirstName = r.FirstName;
userModel.LastName = r.LastName;
});
return listOfUsers.ToList();
However, I ended up using Lucas Roselli's solution.
Update: Simplified by returning an anonymous object:
var listOfUsers = db.Users.Select(r => new
{
FirstName = r.FirstName;
LastName = r.LastName;
});
return listOfUsers.ToList();
Adding this in your Application_Start() method of Global.asax file should solve the problem
protected void Application_Start()
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
// ...
}
METHOD 2: [Not recommended]
If you are working with EntityFramework, you can disable proxy in your DbContext class constructor. NOTE: this code wll be removed if you update the model
public class MyDbContext : DbContext
{
public MyDbContext()
{
this.Configuration.ProxyCreationEnabled = false;
}
}
There's also this scenario that generate same error:
In case of the return being a List<dynamic> to web api method
Example:
public HttpResponseMessage Get()
{
var item = new List<dynamic> { new TestClass { Name = "Ale", Age = 30 } };
return Request.CreateResponse(HttpStatusCode.OK, item);
}
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
So, for this scenario use the [KnownTypeAttribute] in the return class (all of them) like this:
[KnownTypeAttribute(typeof(TestClass))]
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
This works for me!
My personal favorite: Just add the code below to App_Start/WebApiConfig.cs. This will return json instead of XML by default and also prevent the error you had. No need to edit Global.asax to remove XmlFormatter etc.
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Just put following lines in global.asax:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Import
using System.Data.Entity;
Use AutoMapper...
public IEnumerable<User> GetAll()
{
using (Database db = new Database())
{
var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
return users;
}
}
Use the following namespace:
using System.Web.OData;
Instead of :
using System.Web.Http.OData;
It worked for me
Add the below line
this.Configuration.ProxyCreationEnabled = false;
Two way to use ProxyCreationEnabled as false.
Add it inside of DBContext Constructor
public ProductEntities() : base("name=ProductEntities")
{
this.Configuration.ProxyCreationEnabled = false;
}
OR
Add the line inside of Get method
public IEnumerable<Brand_Details> Get()
{
using (ProductEntities obj = new ProductEntities())
{
this.Configuration.ProxyCreationEnabled = false;
return obj.Brand_Details.ToList();
}
}
Use [Serializable] for class:
Example:
[Serializable]
public class UserModel {
public string Name {get;set;}
public string Age {get;set;}
}
It worked for me!
Solution that worked for me:
Use [DataContract] for class and [DataMember] attributes for each property to serialize. This is enough to get Json result (for ex. from fiddler).
To get xml serialization write in Global.asax this code:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
Read this article, it helped me to understand serialization:
https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
To add to jensendp's answer:
I would pass the entity to a user created model and use the values from that entity to set the values in your newly created model. For example:
public class UserInformation {
public string Name { get; set; }
public int Age { get; set; }
public UserInformation(UserEntity user) {
this.Name = user.name;
this.Age = user.age;
}
}
Then change your return type to: IEnumerable<UserInformation>
While all these answers above are correct, one may want to check the InnerException > ExceptionMessage.
If it says something like this "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.". This could be an issue because of default behavior of the EF.
By assigning LazyLoadingEnabled = false in your DbContext constructor will do the trick.
public class MyDbContext : DbContext
{
public MyDbContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
For more detailed reading about EagerLoading and LazyLoading behavior of EF refer this MSDN Article.
In my case I have had similar error message:
The 'ObjectContent`1' type failed to serialize the response body for
content type 'application/xml; charset=utf-8'.
But when I dig deeper in it, the issue was:
Type 'name.SomeSubRootType'
with data contract name
'SomeSubRootType://schemas.datacontract.org/2004/07/WhatEverService'
is not expected. Consider using a DataContractResolver if you are
using DataContractSerializer or add any types not known statically to
the list of known types - for example, by using the KnownTypeAttribute
attribute or by adding them to the list of known types passed to the
serializer.
The way I solved by adding KnownType.
[KnownType(typeof(SomeSubRootType))]
public partial class SomeRootStructureType
This was solved inspired from this answer.
Reference: https://msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx
I basically add one line which they are
entities.Configuration.ProxyCreationEnabled = false;
to UsersController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using UserDataAccess;
namespace SBPMS.Controllers
{
public class UsersController : ApiController
{
public IEnumerable<User> Get() {
using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
entities.Configuration.ProxyCreationEnabled = false;
return entities.Users.ToList();
}
}
public User Get(int id) {
using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
entities.Configuration.ProxyCreationEnabled = false;
return entities.Users.FirstOrDefault(e => e.user_ID == id);
}
}
}
}
You will have to define Serializer Formatter within WebApiConfig.cs available in App_Start Folder like
Adding config.Formatters.Remove(config.Formatters.XmlFormatter);
// which will provide you data in JSON Format
Adding config.Formatters.Remove(config.Formatters.JsonFormatter);
// which will provide you data in XML Format
Another case where I received this error was when my database query returned a null value but my user/view model type was set as non-nullable. For example, changing my UserModel field from int to int? resolved.
This also happens when the Response-Type is not public!
I returned an internal class as I used Visual Studio to generate me the type.
internal class --> public class
Visual Studio 2017 or 2019 is totally unthoughtful on this, because Visual Studio itself requires the output to be in json format, while Visual Studio's default format is "XmlFormat" (config.Formatters.XmlFormatter).
Visual Studio should do this automatically instead of giving developers so much trouble.
To correct this problem, go to the WebApiConfig.cs file, and add
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
after "config.MapHttpAttributeRoutes();" in the Register(HttpConfiguration config) method. This would allow your project to produce json output.
In my case I solved recreating the database.
I made some changes in a model and launching Update-Database in Package Manager Console I got the following Error:
"The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_dbo.Activities_dbo.Projects_ProjectId". The conflict occurred in database "TrackEmAllContext-20190530144302", table "dbo.Projects", column 'Id'."
In case: If adding code to WebApiConfig.cs or Global.asax.cs doesn't work for you:
.ToList();
Add .ToList() function.
I tried out every solution but following worked for me:
var allShops = context.shops.Where(s => s.city_id == id)**.ToList()**;
return allShops;
I hope, it helps.
in my case, it was fixed when I removed the virtual keyword before my navigation properties,
I mean the reference tables.
so I changed
public virtual MembershipType MembershipType { get; set; }
to:
public MembershipType MembershipType { get; set; }

TPH EF6.0: Cannot calculate the value of expression

I have 3 classes inherited from an abstract base class:
abstract class PortTaskStep{
public Guid TaskID { get; set; }
[ForeignKey("TaskID")]
public virtual PortChangeTask PortChangeTask { get; set; }
public virtual String CompleteStep() { return string.Empty;}
}
class PortTaskStep_Add:TaskStep{}
class PortTaskStep_Modify:TaskStep{}
class PortTaskStep_Delete:TaskStep{}
public class HncNetScanContext : DbContext
{
public DbSet<PortTaskStep> PortTaskSteps { get; set; }
}
When I try to get data from table 'PortTaskSteps', the query returns :
cannot calculate the value of the expression
Some more details:
public class PortChangeTask{
public ICollection<PortTaskStep> PortTaskSteps { get; set; }
public Boolean CompleteTask(){
foreach (var portTaskStep in PortTaskSteps)
{
portTaskStep.CompleteStep();
}
}
}
The Domain assembly contains the classes mentioned above are referenced by a web project and a web site. And when I debug the function in the web site, everything goes well.However, when I debug the same function in the web project, the results is null.
The Snapshot
For another scene tested in the Web Project:
As shown in the Snapshot, I try to get "PortTaskSteps". And the result is null. However, after I uncomment the "textQuery", the result is a list of PortTaskStep.
The Snapshot
I think something is going wrong in webconfig or environment of the Web Project, however, I cannot figure out the point...:(
How can I resolve the problem?
Thanks!
Perhaps the right translation might be "Could not evaluate expression". If so, then most likely it's the problem with Visual Studio. Try to delete all your breakpoints from solution and reinsert them. If that doesn't help you, check this link: Visual Studio 2013 'Could not evaluate Expression' Debugger Abnormality.
If the problem for you appears always on both retrieving data and trying to see it in the debug mode then you might want to check your configuration (maybe you've missed mapping or something important for this table).

Peculiar Issue When Using Expressions and (Web) Console Application

I ran into an issue the other day that I first believed to be an issue with Entity Framework. I posted a question about it the other day here. Since then, I have determined that this issue is not related to Entity Framework.
Consider the following classes:
public abstract class ModelBase
{
public Guid Id { get; set; }
}
public class User : ModelBase
{
public string Username { get; set; }
}
public abstract class ModelBaseConfiguration<T> where T : ModelBase
{
public virtual void Configure()
{
ConfigureGuidProperty(e => e.Id);
}
protected void ConfigureGuidProperty(Expression<Func<T, Guid>> expression)
{
Debug.WriteLine(expression);
}
protected void ConfigureStringProperty(Expression<Func<T, string>> expression)
{
Debug.WriteLine(expression);
}
}
public class UserConfiguration : ModelBaseConfiguration<User>
{
public override void Configure()
{
base.Configure();
ConfigureStringProperty(e => e.Username);
}
}
If I add the following code to the Main method of an old Console Application project (the one located under the Windows node in VS2015):
UserConfiguration configuration = null;
configuration = new UserConfiguration();
configuration.Configure();
...and execute it, I get the following output in the debug window:
e => e.Id
e => e.Username
This is what I expect.
Now, if I use the exact same code as listed above in a new ConsoleApplication project (the one located under the Web node in VS2015) and execute it, I get the following output in the debug window:
e => Convert(e).Id
e => e.Username
As you can see, the first line of output is different than before. This is what is causing issues with Entity Framework.
I have discovered that the difference is the project type, being that the code is exactly the same in both scenarios. What I am trying to figure out is why. Why is there an attempted conversion in the expression of the second scenario? Is there something I have been missing for some time now? Is this an issue with the new project type? I am trying to educate myself so that I can adjust if necessary.
This issue is corrected after installing the ASP.NET 5 RC1 update.

Azure Storage Client v4.1 - a value of the non-primitive type was expected

I've recently upgraded my ASP.NET project (MVC5) to target Azure SDK 2.3 with Storage Library 4.1 and am encountering a strange error when I try to save anything to Table Storage.
Error:
An unhandled exception of type 'Microsoft.WindowsAzure.Storage.StorageException' occurred in Microsoft.WindowsAzure.Storage.dll
Additional information: A primitive value was specified; however, a value of the non-primitive type '' was expected.
My models go into table storage via repositories that use a TableServiceContext to add, update, delete, save.
I follow this pattern for my models:
[System.Data.Services.Common.DataServiceKey(new string[] { "PartitionKey", "RowKey" })]
public class PersistedAlert : Alert, ITableEntity
{
public string PartitionKey
{
get { return this.StudentId; }
set { this.StudentId = value; }
}
public string RowKey
{
get { return this.Id; }
set { this.Id = value; }
}
public DateTime Timestamp { get; set; }
public new int Type { get; set; } //hides Enum type in Alert base class
}
During the upgrade I needed to swap out all of my references to
System.Data.Services.*
for
Microsoft.Data.Services.*
...in addition to the OData libraries.
Has something changed internally that makes my pattern no longer valid?
Since there's nothing (yet) on this error on the net, and this is pretty much the only place it's discussed, I'll add a solution even though my context is different than yours. The error is exactly the same so I guess it originates from the same place.
For me, it was a inherited primary key that caused the problem. The primary key of the serialized entity has to be natural and not overriden. If Class has an ID property, DerivedClass will also have to declare the ID property as "new", or the ID property has to be moved from Class to DerivedClass.
Here's more detail: http://jerther.blogspot.ca/2014/12/aspnet-odata-v4-primitive-value-was.html
I do believe this is a bug and not a limitation as the inherited key works very well with Entity Framework and Fluent API.
I hope this helps and saves some hair pulling.
In the end I decided to upgrade all of the repository code to move away from the WCF based TableServiceContext which is deprecated and instead make calls via CloudTable. I can only assume something internally changed to one of the above mentioned libraries that resulted in the problem I was seeing.
In addition to changing the repository code, I also needed to update my entities to inherit from the Azure ITableEntity (I had my own flavour previously), like this:
public class PersistedAlert : Alert, Microsoft.WindowsAzure.Storage.Table.ITableEntity
{
public string PartitionKey
{
get { return this.StudentId; }
set { this.StudentId = value; }
}
public string RowKey
{
get { return this.Id; }
set { this.Id = value; }
}
public DateTimeOffset Timestamp { get; set; }
public string ETag { get; set; }
public void ReadEntity(IDictionary<string, Microsoft.WindowsAzure.Storage.Table.EntityProperty> properties, Microsoft.WindowsAzure.Storage.OperationContext operationContext)
{
Microsoft.WindowsAzure.Storage.Table.TableEntity.ReadUserObject(this, properties, operationContext);
}
public IDictionary<string, Microsoft.WindowsAzure.Storage.Table.EntityProperty> WriteEntity(Microsoft.WindowsAzure.Storage.OperationContext operationContext)
{
return Microsoft.WindowsAzure.Storage.Table.TableEntity.WriteUserObject(this, operationContext);
}
}

Categories