EF Core - Populate an UnMapped column from raw SQL? - entity-framework-core

I have a query where I'm doing a Count on a specific join/column - If I run this code with the "Clicks" [NotMapped] attribute removed, all the values populate properly - but then inserts fail since "Clicks" is not a valid column name. When I mark the column as [NotMapped] then it's not populating from this statement. How can I use raw SQL and populate a [NotMapped] column?
Code:
var query = db.URLs.FromSqlRaw(
#"SELECT [u].[Key], [u].[Url], COUNT(c.Id) AS [Clicks]
FROM[URLs] AS[u]
LEFT JOIN[Clicks] AS[c] ON[u].[Key] = [c].[ShortUrlKey]
GROUP BY[u].[Key], [u].[Url]")
.AsQueryable<ShortURL>();
var urls = query.ToList();
Model (works for inserts, but doesn't populate Clicks property):
public class ShortURL
{
public string Key { get; set; }
public string Url { get; set; }
[NotMapped()]
public int Clicks { get; set; } // doesn't populate from raw query
}
Model (works for queries, but fails on inserts)
public class ShortURL
{
public string Key { get; set; }
public string Url { get; set; }
public int Clicks { get; set; } // not in DB
}

This is not the best method but is an option you can use to solve your problem:
Create a new DbSet in you DBContext list:
public DbSet<YourMode> actual_table_name { get; set; }
public DbSet<CopyWithUnmappedModel> arbitary_table_name { get; set; }
Duplicate the entire YourModel under the YourModel and give it any name you want (CopyWithUnmappedModel)
Remove all the NotMapped fields and place them in the arbitary model with the NotMapped attribute
Use the arbitary_table_name instead of actual_table_name where you want to only Read the date. Since you are not writing to the table the name of the table does not mater. And use the the first one actual_table_name for writing.

Related

Querying for records that must contain a set of values in Entity Framework Core

I'm trying to create a filtering system in a site on a set of records that can be tagged with multiple tags. Eventually I'd like the filter to support OR, AND, and NOT, but right now I'm just trying to get AND working.
Here are the entities with relevant properties, which is just a many to many relationship modeled in EF Core.
public class Record
{
public int Id { get; set; }
public ICollection<RecordTag> RecordTags { get; set; }
}
public class RecordTag
{
public int RecordId { get; set; }
public Song Record { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<RecordTag> RecordTags { get; set; }
}
I tried to write an EF Core query as follows:
var tagList = tags.Split(',');
// tagList now contains the strings of tags the user has filtered on
Records = recordRepository.Query() // simply returns IQueryable<Records>
.Where(r=> tagList.All( // For each record, iterate through all tags selected by the user
t => r.RecordTags.Select(rt => rt.Tag.Name).Contains(t))) // For each user selected tag, get all the tags on the record and ensure it contains the tag we're iterating over
.Include(r => r.RecordTags).ThenInclude(rt => rt.Tag).ToList(); // Include the tag data back with the parent entity.
However, this throws an error
[query] could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
I'd prefer not to have to bring back a larger set and filter it on the application server, rather, having the query be built correctly directly against the DB.
Why is this query not valid? Is there another way to write this so it is?
You can add where conditions in a foreach loop.
var recordRepositoryQuery = recordRepository.Query();
foreach(var tag in taglist)
{
recordRepositoryQuery = recordRepositoryQuery.Where(r => r.RecordTags.Select(rt => rt.Tag.Name).Contains(tag))
}

EF creating an unwanted field in database

I've hit a snag while building a .net mvc site. I have 2 related objects and am struggling with properly linking them. Specifically:
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostCode { get; set; }
[ForeignKey("AddressCategory")] // <-- EF adds field to below object's table
public int AddressCategoryId { get; set; }
public virtual AddressCategory AddressCategory { get; set; }
}
public class AddressCategory
{
public int AddressCategoryId { get; set; }
public string Description { get; set; }
}
Adding the [ForeignKey] data annotation to the Address object results in EF adding an Address_AddressId column to the AddressCategory table, which I don't want (or need) to happen.
I've tried to omit the ForeignKey attribute, but then I run into other errors because .net can't link the tables (e.g. Unknown column 'Extent1.AddressId' in 'field list'). Additionally, I wouldn't be able to use:
var addresses = db.Addresses.Include(l => l.AddressCategory);
Is there any way to link the 2 tables without EF adding an additional column to the AddressCategory table?
Thank you to #cloudikka for responding. After much trial-and-error I seem to have gotten it to work simply by omitting any ForeignKey reference from either object. I let EF rebuild the database and perform all scaffolding (CRUD forms) and they have been created perfectly.
My take-away is that foreign key attributes should be used for parent-child relationships, but not for look-up tables. I clearly have much to learn about asp.net mvc!

How do I add index for a Model First entity in Entity Framework (EF6)?

I'm using EF6.1 with Model First approach, and I want to add an index to some database columns.
Is there a more convenient way than the one described in Add index in EF Model First design?
I would prefer to generate the index in the initial SQL, and avoid a migration.
EF 6.1 added IndexAttribute so that you can specify an index on a property like so:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index]
public int Rating { get; set; }
public int BlogId { get; set; }
}
It is talked about in detail here : http://msdn.microsoft.com/en-us/data/jj591583.aspx#Index

Do all associated objects have to be accessed (lazyloaded) before an existing object can be saved?

I'm learning EF Code First and am having trouble when updating existing records. I've boiled it down to this simple example:
This works:
using(var db = new DataContext()){
var p = db.People.Find(1);
p.Name="New Name";
Console.WriteLine(p.Gender.Name); //<--Unnecessary property access
db.SaveChanges(); //Success
}
...but this fails (when the WriteLine is removed):
using(var db = new DataContext()){
var p = db.People.Find(1);
p.Name="New Name";
db.SaveChanges(); //DbValidationError "Gender field is required."
}
Why do I have to access/load the Gender propery if I'm not using it and the data is already correctly stored in the database? I just want to change the Name on an existing record. In this example, Gender is a one-to-many association stored as Gender_Id in the People table. The classes are defined like this:
public class Person
{
[Key]
public int PersonId { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required, Column("Gender")]
virtual public GenderCode Gender { get; set; }
}
public class GenderCode
{
[Key]
public int Id { get; set; }
[Required, MaxLength(10)]
public string Name { get; set; }
}
public class DataContext:DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<GenderCode> GenderCodes { get; set; }
}
Of course, the fully defined classes are to have many more fields. I'd rather not have to access every dependant property every time I want to modify an unrelated value.
Is there a way to load an object, change a field, and save it without loading all related objects first?
Yes, this is necessary because of some horrible design mistakes in EF.
Check out my similar question, EF: Validation failing on update when using lazy-loaded, required properties
One trick is declaring FK properties along with the OO relations:
[ForeignKey("GenderId"), Column("Gender")]
virtual public GenderCode Gender { get; set; }
[Required]
public int GenderId { get; set; }
It is because you are using data annotations and Required attribute has also meaning for validation. Once you set navigation property as Required by data annotation it must be filled / loaded when you are going to persist entity to the database.

EF CTP4 Missing columns in generated table

I'm having an issue that i just can't seem to figure out. Lets say I have 2 Entities defined in my domain; Person and Document. Below is the definition for Document :
public class Document
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Title { get; set; }
public DateTime Date { get; set; }
public virtual Person Owner{ get; set; }
public virtual Person AssignedTo { get; set; }
}
Now, when EF CTP4 creates the SQL table on initialize, there is only one field mapping to a Person.Id being Owner_id. Whatever i try, the field for AssignedTo is never created.
Anything that could solve this?
Regards,
avsomeren
Your code perfectly created the desired schema in the database for me:
If you don't get this schema in you DB then my guess is that something is not right with the rest of your object model. Could you post your full object model please?
Another Solution:
While your current Document class will give you the desired results, but you can still take advantage of the Conventions for Code First and explicitly specify the FKs for your navigation properties:
public class Document
{
public int Id { get; set; }
[Required][StringLength(255)]
public string Title { get; set; }
public DateTime Date { get; set; }
public int OwnerID { get; set; }
public int AssignedToID { get; set; }
public virtual Person Owner { get; set; }
public virtual Person AssignedTo { get; set; }
}
Code First will now infer that any property named <navigation property name><primary key property name> (e.g. OwnerID), with the same data type as the primary key (int), represents a foreign key for the relationship.
This essentially results to the same DB schema plus you have the FKs on your Document object as well as navigation properties which gives you ultimate flexibility to work with your model.