index attribute not honored by entity framework? - entity-framework

I'm using a model first approach. So, I got a generated class
namespace XXX.GlobalDatabaseModel
{
using System;
using System.Collections.Generic;
public partial class LocalizedFoodGroup
{
public int FoodGroupId { get; set; }
public string Culture { get; set; }
public string LocalizedName { get; set; }
public virtual FoodGroup FoodGroup { get; set; }
}
}
and, in another file in the same solution I want to add an index to it:
namespace XITASO.GlobalDatabaseModel
{
[MetadataType(typeof(LocalNameLocalizedFoodGroup))]
public partial class LocalizedFoodGroup
{
}
public class LocalNameLocalizedFoodGroup
{
[Index("UX_LocalizedFoodGroup",IsUnique = true)]
public string Culture { get; set; }
}
}
Only, when I click "generate database from model" and look at the sql file, no index shows up.
Does anybody know what I'm doing wrong?
Lots of Greetings!
Volker

Related

Scaffolding MVC Controller - how to indicate dataValueField and dataTextField?

One of the overload methods of SelectList (from the Microsoft.AspNetCore.Mvc.Rendering namespace) is defined as:
public SelectList(IEnumerable items, string dataValueField, string dataTextField);
When I scaffold an "MVC Controller with view, using Entity Framework" and I create my CRUD pages, I may see the following method inside of my Controller:
public IActionResult Create()
{
ViewData["Continent"] = new SelectList(_context.Continent, **"ContinentID", "ContinentID"**);
ViewData["Country"] = new SelectList(_context.Country, **"CountryID", "CountryName"**);
return View();
}
The field supplied to the dataTextField parameter is different between Continent/Country. How does MVC/EntityFramework decide which field to supply to dataTextField when scaffolding a Controller? Is there something in the individual models or in the DbContext that I am overlooking? I'd like for the dataTextField of Continent to be "ContinentName" so that I don't have to change it manually in the future when I need to delete and then re-scaffold the Controller.
Edit:
Here are the model definitions:
The Model of the Controller that I posted above:
using System;
using System.Collections.Generic;
namespace Project.Models
{
public partial class ProjectForm
{
public int ProjectFormID { get; set; }
public int ContinentID { get; set; }
public int CountryID { get; set; }
public virtual Continent ContinentNavigation { get; set; }
public virtual Country CountryNavigation { get; set; }
}
}
The one that displays the "CountryName" in the dataTextField the way that I want to see it:
namespace Project.Models
{
public partial class Country
{
public int CountryID { get; set; }
public string CountryName { get; set; }
public virtual ICollection<ProjectForm> ProjectForm { get; set; }
}
}
The one that displays the "ContinentID" in the dataTextField the way that I do NOT want to see it:
namespace Project.Models
{
public partial class Continent
{
public int ContinentID { get; set; }
public string ContinentName { get; set; }
public virtual ICollection<ProjectForm> ProjectForm { get; set; }
}
}
There is nothing obviously different to me in the model definitions unfortunately.
I stumbled across this post today (a bit late), but see it still hasn't been answered.
While I can't say why the scaffolding chose to use one field over another in your scenarios (unless you initially had your class/model written differently the last time you cleaned/built your project), I can say how to force it to use a specific column.
Add the DisplayColumn attribute to your class. You will need to rebuild before scaffolding again for the change to take.
namespace Project.Models
{
[DisplayColumn("ContinentName")]
public partial class Continent
{
public int ContinentID { get; set; }
public string ContinentName { get; set; }
public virtual ICollection<ProjectForm> ProjectForm { get; set; }
}
}

Entity Framework fails to get child elements

I have SQLite db and these EF models and context.
Models and Context
public class CardHolder
{
public int CardHolderId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
public string TenantName { get; set; }
public ICollection<AccessCard> AccessCards { get; set; }
}
public class AccessCard
{
public int AccessCardId { get; protected set; }
public CardHolder CardHolder { get; set; }
public DateTime ActivationDate { get; protected set; }
public bool ActivationProcessed { get; set; }
public DateTime? DeactivationDate { get; protected set; }
public string DeactivationReason { get; set; }
public bool DeactivationProcessed { get; set; }
}
public class MyContext : DbContext
{
public DbSet<CardHolder> CardHolders { get; set; }
public DbSet<AccessCard> AccessCards { get; set; }
}
And the Main program
class Program
{
static void Main(string[] args)
{
using (var db = new MyContext())
{
var cardHolders = db.CardHolders.Include("AccessCard").ToList();
}
}
}
Question1: Why do I get this exception
System.InvalidOperationException: 'A specified Include path is not
valid. The EntityType 'SQLiteDemo.Models.CardHolder' does not declare
a navigation property with the name 'AccessCard'.'
If I replace it with
var cardHolders = db.CardHolders.Include("AccessCards").ToList();
I get another error:
SQL logic error no such column: Extent2.CardHolder_CardHolderId
What is wrong with Entity Framework?
Question2: Why cant I use arrow function in Include statement, it doesnt compile at all?
var cardHolders = db.CardHolders.Include(x => x.AccessCards).ToList();
Question3: Why do I need to use Include at all if my ICollection association property AccessCards is NOT virtual - that means eager loading must work by itself!
Why the hell it is so problematic and buggy? Nothing works as it should :(
1 - You have a typo as you have already determined :)
1B - "SQL logic error no such column: Extent2.CardHolder_CardHolderId"
EF isn't finding your FK. You could add it to your AccessCard model:
public int CardHolderId { get; set; }
2 - You need to pull in the LINQ extensions. Make sure you have both of these using statements at the top:
using System.Data.Entity;
using System.Linq;
3 - You, like many others, are misunderstanding lazy loading. Eager loading still requires an Include() to fetch related data. Lazy loading only fetches the relations when you access them.

entity framework cannot get results of a many to many relationship

I was trying to explore Code First today when I ran into a weird situation. I created 3 tables and entity framework created a 4th table for my many-to-many relationship. the code is as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pizza_test.models
{
public class Pizza
{
public int Id { get; set; }
public string Name { get; set; }
public float Price { get; set; }
public string Description { get; set; }
public int IngredientId { get; set; }
public virtual IList<Bottom> Bottoms { get; set; }
public virtual IList<Ingredient> Ingredients { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pizza_test.models
{
public class Bottom
{
public int Id { get; set; }
public string Name { get; set; }
public int Size { get; set; }
public virtual Pizza Pizzas { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pizza_test.models
{
public class Ingredient
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual Pizza Pizzas { get; set; }
}
}
The code of my dbcontext is as follows:
namespace pizza_test
{
using models;
using System;
using System.Data.Entity;
using System.Linq;
public class PizzaContext : DbContext
{
public PizzaContext()
: base("name=PizzaContext")
{
}
public DbSet<Pizza> Pizza { get; set; }
public DbSet<Ingredient> Ingredient { get; set; }
public DbSet<Bottom> Bottom { get; set; }
}
}
However when I try to query the database in my program.cs I am not allowed to call item.Ingredients.Name .
class Program
{
static void Main(string[] args)
{
var _context = new PizzaContext();
var firstPizza = _context.Pizza.Include(p => p.Ingredients).ToList();
foreach (var item in firstPizza)
{
Console.WriteLine(item.Ingredients.Name);
}
}
}
Which makes me kind of lost since i thought this was the power of ORMs. Did i forget something here? Also when i debug it in Visual Studio I can find all the related data of Ingredients including name, description and id. Thanks for any help provided.

DataAnnotations with EDMX EF 6.0 does not work

I have an .EDMX with my models, from EF 6.0, and I want to add attributes to some of my fields. I've read many examples where they use DataAnnotations with MetadataType... I've tried to implement it, but it does not override... For example if I have
[Queryable]
public string Name;
it will not work.
but if I have
[Queryable]
public string Name2;
It will work and I will see Name2 as part of the attributes!
The code I use in order to find those attributes is as follow :
var properties = typeof(TEntity).GetProperties().Where(prop => prop.IsDefined(typeof(QueryableAttribute), false));
Like I said, when I have Name2, i can find it in the attributes list. And when I have Name, I don't ...
here is my 3 files, they are both in "MMS.Entities" namespace
AreaMetadata.cs
namespace MMS.Entities
{
[MetadataType(typeof(AreaMetadata))]
public partial class Area
{}
public class AreaMetadata
{
[Queryable]
public string Name;
[Queryable]
public string Abbreviation;
[Queryable]
public string Description;
}
}
Area.cs
namespace MMS.Entities
{
using MMS.Common.Utilities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class Area : Entity, IEntity
{
public Area()
{
this.Plants = new HashSet<Plant>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public bool IsDeleted { get; set; }
public int UserCreatedId { get; set; }
public Nullable<int> UserModifiedId { get; set; }
public System.DateTime DateCreated { get; set; }
public Nullable<System.DateTime> DateModified { get; set; }
public virtual ICollection<Plant> Plants { get; set; }
}
}
Should the name of AreaMetadata.cs be different? Should I include anything somewhere in order to make them both work together?
Thanks for your advices!
I believe a similar question has been answered here.
When you try to access a MetadataType property attribute, you have to get to the MetadataType using reflection, and not simply the class that uses the MetadataType. There is an explanation in the link I provided and an example of how to get it.
Basically, use reflection to get the AreaMetadata properties, not the Area properties.
Try:
MetadataTypeAttribute metadata = typeof(TEntity)
.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
.OfType<MetadataTypeAttribute>().ToArray().FirstOrDefault();
PropertyInfo[] properties = metadata.MetadataTypeClass.GetProperties()
.Where(prop => prop.IsDefined(typeof(Queryable), false));

How to Define EF5 Navigation Property to Multiple Types

I have a shared type that I'd like to represent in a database. I need to know either if it's possible with code first or if it's bad design. (Please point to sources of your reasoning).
Here's the layout:
public class A
{
public Guid Id;
public Guid ParentId; // Points to either B or C
public string Foo;
}
public class B
{
public Guid Id;
public virtual ICollection<A> ManyA { get; set; }
// Other fields
}
public class C
{
public Guid Id;
public virtual ICollection<A> ManyA { get; set; }
// Other fields
}
I've managed GUID PKs by using the [DatabaseGenerated(DatabaseGeneratedOption.Identity)] annotation, which I believe would make this possible.
The goal is to keep the A-table simple; I'd like it not to have B_Id and C_Id columns, as I think they're unnecessary, and there may be a D type in the future that has many A just like B and C. Also, this scenario is used for a few other relationships.
I think the spirit of the problem is apparent, however the EF-specific class members are just there for illustration. If they need changing, so be it.
(I'm sorry for the contrived example, however exposing the true nature of this, IMO, doesn't clarify the problem).
Also, I tried searching for hours! I don't know the proper nomenclature to find a suitable answer. I'm pretty new to databases and EF in general.
Don't use Guid to make a index, why you choose the hard-way ? :S
use the DataAnnotations like the example:
A class:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Web.Mvc;
namespace app.Models
{
[Table("A")]
public class A
{
[Key]
public int AId { get; set; }
[Required(ErrorMessage = "Campo obrigatório")]
[DisplayName("Title")]
[StringLength(100)]
public string Title { get; set; }
public virtual ICollection<B> bCollection { get; set; }
}
}
B class:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Web.Mvc;
namespace app.Models
{
[Table("B")]
public class B
{
[ScaffoldColumn(false)]
[Key]
public int BId { get; set; }
[DisplayName("Description")]
[StringLength(100)]
public string Description { get; set; }
public virtual ICollection<A> AList{ get; set; }
}
}
You can see more details here: link!!!
For more details about EF5 Data Annotations: link
I've found that I can make a common base type for both B and C types. For example:
class Program
{
static void Main(string[] args)
{
using (var context = new Ctx())
{
var b = new B();
var c = new C();
b.ManyA.Add(new A());
c.ManyA.Add(new A());
context.Cs.Add(c);
context.Bs.Add(b);
context.SaveChanges();
}
}
}
public class Ctx : DbContext
{
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
public DbSet<C> Cs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseClass>()
.HasMany<A>(b => b.ManyA)
.WithRequired(a => a.BaseObject)
.WillCascadeOnDelete(true);
}
}
public class BaseClass
{
public BaseClass() { ManyA = new HashSet<A>(); }
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual ICollection<A> ManyA { get; set; }
}
public class A
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public BaseClass BaseObject { get; set; }
public string Foo { get; set; }
}
public class B : BaseClass
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string SomeProperty { get; set; }
}
public class C : BaseClass
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public int SomeOtherProperty { get; set; }
}
And, based on this Msdn article, would result in Table Per Hierarchy database layout. TPH performs better versus TPT, which I was thinking could be an answer.
I was really hoping for something simpler, but performance is the bottom line, afterall