I am using EF5 and .NET 4.5 targeting an Oracle 11g database through Oracle.ManagedDataAccess.Client. I set up a small table to test and the how it works.
Now here is a weird fact which shows no result on searching the web nor this site. On every query I have a last column like "Extent1"."Text_TextID"!!! This obviously makes Oracle to throw an error Invalid identifier as I have no column with such name nor another object in the database.
This happens no matter how many tables/columns I have and no matter how I name them (if I have several tables all will have this extra column in the query).
Anybody has any idea why this happens??
Sample code below:
//POCO class and mapping
[Table("LO_USERS")]
public class User
{
[Key]
[Column("USER_ID")]
public int UserID { get; set; }
}
//define the context
public class TestContext : DbContext
{
public TestContext():base("OracleConn")
{
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//replace the annoying dbo schema name. Note: even if I remove this, I still get the extra column in the query
modelBuilder.Entity<User>().ToTable("LO_USERS", "TEST_SCHEMA");
}
//create a new user
using (var db = new TestContext())
{
var user = new User();
db.Users.Add(user);
//here I set a breakpoint
db.SaveChanges();
}
The query as showing by VS2012 at the breakpoint:
SELECT
1 AS "C1",
CAST( "Extent1"."USER_ID" AS number(10,0)) AS "C2",
"Extent1"."Text_TextID" AS "Text_TextID"
FROM "TEST_SCHEMA"."LO_USERS" "Extent1"
Edit:
It is the same with EF6 and DotConnect.
I found it: the problem was I was referencing User class in another class as child object, like
public class Text
{
public virtual ICollection<User> Users { get; set; }
without specifying any foreign key column in user class and EF was trying to set one by its own.
Once I removed the line above the extra column disappeared from the select statement.
Related
I'm having trouble implementing the many-to-many relationship using the Entity Framework Core 5 in Visual Studio.
I have the classes:
public class Medico
{
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public int Id { get; set; }
public string Nome { get; set; }
public int CRM { get; set; }
public List<Especialidade>Especialidades { get; set; }
public class Especialidade
{
public int Id { get; set; }
public string Descricao { get; set; }
public IList<Medico>Medicos { get; set; }
}
And the Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Nome,CRM")] Medico medico)
{
var lstTags = Request.Form["chkTags"];
if (!string.IsNullOrEmpty(lstTags))
{
int[] splTags = lstTags.ToString().Split(',').Select(Int32.Parse).ToArray();
if (splTags.Count() > 0)
{
var medicoEspecialidades = await _context.Especialidades.Where(t => splTags.Contains(t.Id)).ToListAsync();
foreach (var me in medicoEspecialidades)
{
medico.Especialidades.Add(me);
}
}
}
if (ModelState.IsValid)
{
_context.Medicos.Add(medico);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(medico);
}
But when I run Create, it returns me with the following error:
"Cannot insert explicit value for identity column in table 'Especialidades' when IDENTITY_INSERT is set to OFF."
If I turn off the Identity_Insert of the Specialty table in the bank, it even inserts, but duplicates the records in the Specialty table.
I've been researching and trying to find a solution for 2 days now. Can someone who has been through this give me a hand?
The application source code is here: https://www.dropbox.com/s/xn6b95h7amfpuqa/AppCompleta%205.0.rar?dl=1
The approach looks Ok, though I would check to ensure that the medico being passed in does not have any Especialidade somehow coming in from the client as these would be detached entities. The error seems to imply that Medico may have a detached Especialidade in its collection. If the checked values represents everything that should be tracked, then this collection should be cleared and the Especialidade references added.
Do you have any explicit mapping configuration for either of these entities? If not I would highly recommend using one for Many-to-Many relationships as sometimes EF can default to unexpected schema assumptions when working off convention, especially in Code First if that is the case. I would look at your schema carefully to ensure it is matching what would be expected for a Many-To-Many. For example, what is the linking table name for Medico-Especialidade? Is there an entity defined for it in the configuration? This is entirely optional and EF should work it out, however if you do have explicit mapping that might not be configured correctly, tripping up the relationships.
One other detail giving off a smell:
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public List<Especialidade>Especialidades { get; set; }
This should be:
public ICollection<Especialidade> { get; set; } = new HashSet<Especialidade>();
EF can work with lists, but when it comes to proxies and the behind the scenes EF is doing with entities it is generally better to declare your collection references as ICollection rather than concrete classes. ToListing a HashSet merely produces a List, so either = new HashSet<Especialidade>() or = new List<Especialidade>() will do. The difference would merely be the behaviour of the collection when you are populating it after "newing" up a Medico, or deserializing one.
I'm using the SimpleMembership.MVC3 package with my MVC3 application and I want to be able to access users from the table through Entity Framework
In examples for doing this with MVC4, you can simply create a POCO to mirror the User table that's been generated, add your DbSet in your DbContext implementation and then query the DbSet like you normally would, ie: context.Users.
This collection is always returning 0 items for me even though there are rows in the table. What am I doing wrong? Here's what I got so far:
[Table("User")]
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
In my service:
model.Accounts = context.Users.ToList();
Thanks!
You do not create your a POCO that mirrors the User table in order to access it directly from EF. There is already a POCO created by the Internet template when you created the project, which you can customize as described here. This same article also shows how you can access the user information by accessing EF directly. You do not create your own context, there is one already in place that you use. Here is a code snippet from that article.
var context = new UsersContext();
var username = User.Identity.Name;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
var email = user.Email;
The article also has links to download the source code that demonstrates the details on how to do this.
I circumvented the membership classes entirely and implemented a pure EF membership system. I leveraged the System.Web.Helpers Crypto helpers to handle password hashing and just create the AuthCookie when needed.
Issue:
The user creates a new account. One of the required fields is BusinessGroup. BusinessGroup is a navigation reference property. The user select the BusinessGroup from a drop down box, the code searches for the BusinessGroup in the database, retrieves it and assigns it to the Account. Sounds pretty simple, right?
For some reason every time you save a new account it also inserts another BusinessGroup in the database table for BusinessGroups even though it already exists as I retrieved it from the database and assigned it directly yo the account. EF context still thinks it is a new one.
BTW, I use Code-First as I am following TDD approach.
Here is my POCO:
[Table("Account")]
public class Account
{
[HiddenInput]
public int Id { get; set; }
[Required(ErrorMessage = "The Name is required")]
public string Name { get; set; }
public Guid? BusinessGroupId { get; set; }
[Required(ErrorMessage = "The Business Group is required")]
public BusinessGroup BusinessGroup { get; set; }
}
Because I want to override the naming convention of the foreign key that is generated in the database I also specified the foreign key, BusinessGroupId above.
Here is the BusinessGroup POCO:
[Table("BsuinessGroup")]
public class BusinessGroup
{
[HiddenInput]
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
}
Here is the code that fetches the BusinessGroup from the database based on what the user selected from a drop down box, the Id is as GUID (note that this code lives inside an MVC custom model binder so it exposes the ModelBinderContext:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
//see if there is an existing model to update and create one if not
var account = (Account) bindingContext.Model ?? new Account();
//find out if the value provider has a required prefix
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
var prefix = hasPrefix ? String.Format("{0}.", bindingContext.ModelName) : String.Empty;
_context = bindingContext;
_prefix = prefix;
//map the fields of the model object
account.Id = Convert.ToInt32((GetValue("Id")));
account.Name = (GetValue("Name"));
account.Phone = (GetValue("Phone"));
account.Fax = (GetValue("Fax"));
account.Email = (GetValue("Email"));
account.Website = (GetValue("Website"));
account.Audit = new Audit{Created = DateTime.Now, CreatedBy = "renso"};
//I changed this to assign the ID rather than assign the BusinessGroup itself since that did not work
//and causes a new BusinessGroup to be inserted into the DB on every save of an account,
// this solution seems to work but I am not sure if this is the correct behavior.
**var tempBusinessGroup = ((Controllers.AccountController)controllerContext.Controller).
BusinessGroupRepository.Find(GetGuidValue("BusinessGroup"));
account.BusinessGroupId = tempBusinessGroup.Id;**
return account;
}
private Guid GetGuidValue(string key)
{
var vpr = _context.ValueProvider.GetValue(_prefix + key);
return vpr == null ? new Guid() : new Guid(vpr.AttemptedValue);
}
private string GetValue(string key)
{
var vpr = _context.ValueProvider.GetValue(_prefix + key);
return vpr == null ? null : vpr.AttemptedValue;
}
.............
}
The code in bold above where I assign a value (GUID) to the BusinessGroupId rather than the BusinessGroup seems to work correctly. Is that the intended behavior of EF, a bug or what?
If I changed it to the code below it causes the issue where a new BusinessGroup gets created when I save the new account:
account.BusinessGroup = ((Controllers.AccountController)controllerContext.Controller).
BusinessGroupRepository.Find(GetGuidValue("BusinessGroup"));
Here is my Action:
[HttpPost]
public ActionResult Create(Account account)
{
if (ModelState.IsValid)
{
AccountRepository.InsertOrUpdate(account);
AccountRepository.Save();
return RedirectToAction("List");
}
var viewData = new AccountControllerViewData { Account = account, BusinessGroups = BusinessGroupRepository.All };
return View(viewData);
}
The save also fails, for some reason I need to switch off ValidateOnSaveEnabled=false in the Save on the AccountRepository, not sure why for it to work at all, it complains about the BusinessGroup being missing as I set the BusinessGroupId property of the account:
public void InsertOrUpdate(Account account)
{
if (account.Id == default(Int32))
{
// New entity
_context.Account.Add(account);
}
else
{
// Existing entity
_context.Entry(account).State = EntityState.Modified;
}
}
public void Save()
{
//need the validation switched off or it fails with an error
_context.Configuration.ValidateOnSaveEnabled = false;
_context.SaveChanges();
}
It feels like I am missing something here. I could not find the answer in EF 2ed, Code-First or DbContext books. It seem when you define your own foreign keys in your POCO, then whenever you want to assign a navigation reference to in my example from an Account to a BusinessGroup, you cannot assign to the BusinessGroup directly, you have to asign a value/key to the foreign key, in this example BusinessGroupId. Am I doing something wrong?
Most likely your BusinessGroupRepository and your AccountRepository are having their own context instances. When you load the BusinessGroup from the BusinessGroupRepository it happens in another context than the context in AccountRepository where you save the new account with. For the second context BusinessGroup is an unknown entity and EF will save it as a new entity.
Refactor your code to make sure that both repositories will use the same context instance. The context should not be created within the repositories but created outside and injected into both instead. Alternatively merge the two repositories into one single repository.
Indeed, re-factored the code so that it uses the same DbContext to retrieve the account and business group, that works as expected, Thanks! The question now is that with my repository pattern, I inherit from an abstract class that news up a new context for me, but that still wont solve the problem that I have that each repository has its own context, how did you guys resolve that issue? For example you create a new account, it has a navigation reference for business group, country, state (like PA, NJ), and others, each one has its own repository, for example to maintain lists of countries in the database. An account has a country navigation reference, what pattern should one follow to share the same context, one per http request I would think?
I'm trying to make EntityFramework work with ASP .NET MVC3 using this tutorial:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
Ok, I have my database, my .edmx model, model classes but one first thing I don't get is:
How does my DbContext derived class even know my .emdx model ? I don't fine where the "link" is created in this tutorial (maybe having several thing with the same name "SchoolContext", for the context as for the connexionstring is confusing ...)
When I run what I got for now with the code:
MMContext context = new MMContext();
List<EntityUser> testList = (from u in context.Users
select u).ToList();
I get:
System.Data.Edm.EdmEntityType: : EntityType 'EntityUser' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �Users� is based on type �EntityUser� that has no keys defined.
Thank you for your help.
Assuming you are using the Code-First approach, you have to define a Key in your Users class:
public class User
{
public int Id { get; set; }
// ...
}
As mentioned from Kyle, if your ID field is not named "Id" you have to add the [Key] attribute:
using System.ComponentModel.DataAnnotations;
public class User
{
[Key]
public int u_Id { get; set; }
// ...
}
The table has two columns.
columns1= IdGroup
columns2= Hgroup
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.SetData();
}
private void SetData()
{
using (DataClass.DataClassesAccDataContext DataContext = new DataClass.DataClassesAccDataContext())
{
List<Group> myGroup = (Group)DataContext.tblGroup.ToList();
}
}
}
public class Group
{
public string Name { get; set; }
public string Family { get; set; }
public bool Selecting { get; set; }
}
error :
Cannot convert type 'WpfA.DataClass.tblGroup' to 'WpfA.Group'
A tblGroup, which is what gets returned from your LINQ query, is not an instance of Group, because tblGroup does not derive from Group. You have a couple of options:
Change Group to being an interface, and have tblGroup implement this
interface (assuming tblGroup has all the properties specified in
Group).
Use a LINQ Select to project out Group objects instead of
tblGroup objects. However note that these Group objects would be
disconnected from the database -- e.g. if you needed to save changes
to the Group objects you would have to copy them back into tblGroup
objects.
(Note also that tblGroups.ToList()[0] will return a single item, not a list. So you should probably be trying to assign it to a Group rather than a List<Group>. Also, if you only want the first result, it's more efficient to use the LINQ First() operator rather than ToList()[0]; this will avoid fetching unwanted records from the database.)