fetch data from associated tables having foreign key in between them - json

there are two tables named project and city like this:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public int CityID { get; set; }
public City City { get; set; }
}
public class City
{
[Key]
public int CityID { get; set; }
public string CityName { get; set; }
public ICollection<Project> Projects { get; set; }
}
Here, CityID in the project class is the foreign key to City class joining both tables.I have properly configured and checked that both are inter connected to each other.
Now, i have a Json action method to fetch the Desired property like this:
public JsonResult GetProjects()
{
var ret = (from project in db.Projects.ToList()
orderby project.ProjectId
select new
{
CityName = project.City.CityName,
ProjectId = project.ProjectId
}).AsEnumerable();
return Json(ret, JsonRequestBehavior.AllowGet);
}
here, i am trying to push out the cityName but i am unable to get back cityname.It is giving System.NullRefernceException at line CityName = project.City.CityName.Please suggest me what to do now. Is something wrong in my code. i am able to get other properties.

Whe you use somehting like .ToList(), .ToArray(), and so on, you are materializing the query. In LINQ to EF materailizing the query means running the SQL query and populating your classes with the data received form the DB. From that point on, there is no chance that the following referred properties are retrieved from the dtabase.
LINQ to EF uses IQueryable<T> interface. An IQueryable<T> is a "potential query" that has not already been "executed". While you do things that doesn't materialize the query, your query will keep being a IQueryable<T> and it won't be executed in the DB.
There is also another thing that materializes the queryable: enumerating it.
So, what you need to is to not materialize the query until you have provided all the information necessary to run the query. In this case you have to remove the .ToList()

Related

How to create a dependency graph using composite keys in EF Core

Trying to store a composite key table which is keyed for both fields to the table it defines dependencies for.
Example case
Import files: 1..10
Dependencies 1: 2,3; 2: 4,5; 4:10
Intent is to use this key-only table for code to do code first strongly typed definitions while also being light weight, and it seemed like the most straight forward way to do it before running into problems.
Current code:
public class ImportFileDependency
{
[Key]
[ForeignKey("ImportFile")]
public int ImportFileId { get; set; }
[ForeignKey("Id")]
public ImportFile ImportFile {get; set;}
[Key]
[ForeignKey("ImportFile")]
public int ImportFileDependencyId { get; set; }
[ForeignKey("Id")]
public ICollection<ImportFile> ImportFileDependencies { get; set; }
}
public class ImportFile
{
[Key]
public int Id {get; set;}
public string Name { get; set; }
public string WorkbookTab { get; set; }
public string File { get; set; }
public ICollection<ImportFileDependency> Dependencies { get; set; }
}
...
modelBuilder
.Entity<ImportFileDependency>(e =>{
e.HasKey(ifd => new { ifd.ImportFileId, ifd.ImportFileDependencyId });
e.HasOne(ifd => ifd.ImportFile)
.WithMany(i => i.Dependencies);
});
modelBuilder
.Entity<ImportFile>()
.HasMany(i => i.Dependencies)
.WithOne()
.HasForeignKey(z => z.ImportFileId);
...
After multiple revisions of following the responses of the add-migration exception response, currently on:
There are multiple properties pointing to navigation 'ImportFile' in entity type 'ImportFileDependency'. To define composite foreign key using data annotations, use ForeignKeyAttribute on navigation.
which did not update from the most recent iteration.
I seem to have recursed into a deadend so looking for guidance
Given the time you've asked it, you probably found the answer yourself or gave up on it, but if someone else struggles with this error, this solved my issue: Entity Framework Code First - two Foreign Keys from same table
You have to define the relationship using fluent API.

ASP.Net Core - EntityFrameworkCore data is not adding, updating instead

I am using ASP.Net Core 2 and Entity Framework Core with MySQL.
I want to add a simple entity to the database.
My Model is like this-
public class Employee
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string City { get; set; }
[Required]
public string Department { get; set; }
[Required]
public int Salary { get; set; }
}
I have configured fluent API in DBContext like this-
//Key automatic generation configuration
modelBuilder.Entity<Employee>()
.Property(b => b.Id)
.ValueGeneratedOnAdd();
And then calling from controller to add content like this-
Employee employee = new Employee
{
City = newString,
Department = newString,
Name = newString,
Salary = DateTime.UtcNow.Millisecond
};
_context.Employee.Add(employee);
_context.SaveChanges();
What I am seeing is updating the first data having ID = 1 and never add new data. I want my ID to be auto-incrementing and don't want to use GUID for maintaining ID. What do I need to do to make it work?
Update-
I have already tried-
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
And
[Key]
public int Id { get; set; }
And
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
But no luck.
The complete code is in this Github Repository.
And the controller is here.
Use the annotation "DatabaseGeneratedOption.Identity" for auto-increment.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
Migration File should be generated as below:
CreateTable(
"dbo.table",
c => new
{
Id = c.Int(nullable: false, identity: true),
.............
})
.PrimaryKey(t => t.Id);
you are mixing data annotations and fluent api configurations which is not a good practice. anyway, entity framework considers int Id in persistence models as key in general so you don't have to specify it manually. I recommend you choosing only one approach (from my experience fluent api is usually better - more extensible for future and persistence models look much cleaner) and try to go without specifying that the Id should or should not change because without it you will get what you want.
your migration should look like this:
migrationBuilder.CreateTable(
name: "TableName",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn)
},
constraints: table =>
{
table.PrimaryKey("PK_TableName", x => x.Id);
});
...

EntityFramework Include and possibly join?

I have the following table structure as shown in the picture. (see: Table structure). Both tables ("Batches" and "Methods") reference to a "Project" table.
When I now create a new Project I would like to get all childs created as well.
Doing so I did the follwoing:
_dbContext.Projects.Where(x => x.Id == prjId)
.Include(x => x.Batches)
.Include(x => x.Batches.Select(y => y.Measurements))
.Include(x => x.Methods).AsNoTracking().FirstOrDefault();
Now the problem is the following:
New Batch and Method instances are created - thus they get a new ID(PK). The referenced Project_Id (FK) is set correct. But in my new Measurement instance only the Batch_Id(FK) is set correct and the Method_Id remains unchanged (has the old value) (see: result).
What I need is that the Measurements.Mehtod_Id is set from the Methods table. Is there any suitable solution for that?
My entities look like the following
public class Project
{
[Key]
public long Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public virtual List<Batch> Batches { get; set; }
public virtual List<Method> Methods { get; set; }
}
public class Batch : BaseObject
{
public Batch()
{
BatchFiles = new List<FileAttachment>();
Measurements = new List<Measurement>();
}
public long Id { get; protected set; }
public long Project_Id { get; set; }
public virtual Project Project { get; set; }
public virtual List<Measurement> Measurements { get; set; }
}
public class Method : BaseObject
{
public Method()
{
Parameters = new List<Parameter>();
}
public long Id { get; protected set; }
public long Project_Id { get; set; }
public virtual Project Project { get; set; }
public virtual List<Measurement> Measurements { get; set; }
}
public class Measurement
{
public int Id { get; protected set; }
[ForeignKey("Batch")]
public long? Batch_Id { get; set; }
[Required]
public virtual Batch Batch { get; set; }
[ForeignKey("Method")]
public long? Method_Id { get; set; }
public virtual Method Method { get; set; }
}
// creation code (just a copy with new IDs for all childs)
Project newProjectVersion = _dbContext.Projects.Where(x => x.Id == prjId)
.Include(x => x.Batches)
.Include(x => x.Batches.Select(y => y.Measurements))
.Include(x => x.Methods)
.AsNoTracking().FirstOrDefault();
_dbContext.Projects.Add(newProjectVersion);
_dbContext.SaveChanges();
Thanks for any help!
The first problem is that your Select statement doesn't connect Measurements to Methods because of the AsNoTracking() addition. Only Projects and Methods are connected because they are explicitly Included off of the Project entity. The Measurements have a Method_id but this is value is not accompanied by a Method in their Method property. You could check that in the debugger if you walk through the object graph (with lazy loading disabled though!). Because of this, when all entities will be Add-ed to the context, EF won't notice that measurements receive new methods.
You could get tempted to fix that by Include-ing Measurement.Method as well:
...
.Include(x => x.Batches.Select(y => y.Measurements.Select(m => m.Method)))
...
Now you'll see that Measurement.Method will be populated everywhere in the object graph.
However, there's a gotcha here. When using AsNoTracking, EF6 doesn't keep track of entities it materialized (duh). This means that for each Measurement it creates a new Method instance, even if an identical Method (by id) was materialized before for another Measurement. (And in this case it will always materialize duplicates, because you already include Project.Methods.)
That's why you can't do this in the quick way with AsNoTracking and Add using one context instance. You'll get an error that EF tries to attach duplicate entities.
You must build the object graph using one context, with tracking, so EF will not materialize duplicates. Then you must Add this object graph to a new context. Which will look like this:
Project project;
using(var db = new MyContext())
{
db.Configuration.ProxyCreationEnabled = false;
project = db.Projects.Where(x => x.Id == prjId)
.Include(x => x.Batches)
.Include(x => x.Batches.Select(y => y.Measurements))
.Include(x => x.Methods).FirstOrDefault();
}
using(var db = new MyContext())
{
db.Projects.Add(project);
db.SaveChages();
}
Three remarks:
Proxy creation is disabled, because you can't attach a proxy to another context without explicitly detaching it first.
No, I didn't forget to include Measurement.Method. All methods are loaded by including them in the Project and now (because of tracking, and assuming that measurement will only have methods of the project they belong to), EF connects them with the Measurements by relationship fixup.
EF-core is smarter here: when adding AsNoTracking it won't track materialized entities, but still, it won't create duplicates either. It seems to have some temporary tracking during the construction of an object graph.
thanks for your answer so far. This works quite fine right now. Unfortunately I noticed that the Measurements entity has another required relationship to a table named 'MeasurementTypes':
[Required]
public virtual MeasurementType MeasurementType { get; set; }
[ForeignKey("MeasurementType")]
public long MeasurementType_Id { get; set; }
In contrast to Batches and Methods these entries must not be copied and the entries already exist in the MeasrementTypes table.
What would be a good way to put the required reference to the Measurements?

Linq to SQL - Best way to work with a collection of objects inside another object

I have a table of Items for auction and a table of bids made for those items. There's much more to the database but we'll keep it simple.
public class Items
{
public int ItemID { get; set; }
public string ItemName { get; set; }
public List<Bid> Bids { get; set; }
}
public class Bids
{
public int BidID { get; set; }
public int ItemID { get; set; }
public decimal Amount { get; set; }
public datetime BidTime { get; set; }
public int CustomerID { get; set; }
}
I want to return a dataset that includes the ItemID, the ItemName and all of the associated bid records ordered by BidTime, descending. Finally, I'd like to only see Items that a certain Customer has bid on and I'd like to only see their bids for that item. There is a foreign key relationship between Bids.ItemID and Items.ItemID. I'm using Linq to SQL.
This works and appears to return the correct dataset:
from i in Items
from b in i.Bids
where i.AuctionID == 2 && b.CustomerID == (Int32?)1165
orderby b.BidTime descending
select new
{
i.ItemID,
i.ItemName,
i.Bids
}
I'm a SQL guy trying to wrap my head around the OO nature of Linq. Is this the best way (or even a good way) to get the results I want? Is there a better way?
Thanks,
BK
How about this one (slightly more simple syntax):
Updated where clause due to comment
from i in db.items
where i.AuctionID == 2 && i.Bids.Any(c=>c.CustomerID == (Int32?)1165)
select new
{ i.ItemId,
i.ItemName,
i.Bids.Where(b=>b.CustomerID == (Int32?)1165 ).OrderbyDescending(b=>b.BidTime)
}
Make sure you profile the sql and verify that you get only ONE sql statement. If not, you need to have to add LoadOptions to your datacontext (that will make sure all the bids are eager loaded with your items).
This one will also order the bids per item (not sure if yours will do the same.... but I guess you checked that).

Entity Framework 4.1 Problems Updating Foreign Key Properties

I am working on an application using Entity Framework 4.1 with DbContext API, in a disconnected environment. I have two basic entities, Person and Degree. Degree has a non-mandatory one-to-many relationship to Person.
The issue is occurring when I update the DegreeId property on the Person entity to a different value. When I save the changes, EF generates an Update statement on the actual Degree table. This in turn causes a concurrency error violation when two or more users are using the application. I was able to find the issue while using SQL Profiler. I’ve tried several configuration variations using the Fluent API, but nothing seems to suppress the additional Update statement on the Degree table.
Here are my entities:
public partial class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public Nullable<int> DegreeId { get; set; }
public Degree Degree { get; set; }
}
public partial class Degree
{
public int DegreeId { get; set; }
public string Name { get; set; }
}
In my Repository class, I am loading the Person object graph as such:
public override Person GetById(int id)
{
return DataContext.People
.Include(d => d.Degree)
.FirstOrDefault(x => x.PersonId == id);
}
In my Service layer, I am getting a person record, and then updating the DegreeId property to a specific value. Note: UnitOfWork.Commit method exposes SaveChanges on DbContext.
using (var unitOfWork = IoC.Resolve<IUnitOfWork>())
{
var personRepository = new PersonRepository(unitOfWork);
var person = personRepository.GetById(240);
person.DegreeId = 1;
personRepository.Update(person);
unitOfWork.Commit();
}
My repository update method attaches the person entity and marks the entity state as modified:
var state = DataContext.Entry(entity).State;
dbSet.Attach(entity);
DataContext.Entry(entity).State = EntityState.Modified;
Here is the SQL statement found in the Profiler session:
exec sp_executesql N'declare #p int
update [Client].[Degree]
set #p = 0
where (([DegreeId] = #0) and ([RowVersion] = #1))
select [RowVersion]
from [Client].[Degree]
where ##ROWCOUNT > 0 and [DegreeId] = #0',N'#0 int,
#1 binary(8)',#0=1,#1=0x0000000000004469
Does anyone know how to stop EF from sending this update statement to SQL Server? Is there something apparent in my entity configuration that causes EF to assume the Degree is also affected?
Thank you.
I was able to find the cause of this issue and prevent it from occurring, but I cannot really explain why it was occurring.
My tables include a TimeStamp column and a corresponding property in the base class for my entities.
I did not show the base class in my original question because it only includes the RowVersion and other audit properties, which I assumed were irrelevant.
One would think I would've learned by know not assume anything about Entity Framework.
Here is my base class definition for the Degree entity:
public abstract class EntityBase : ValidableObject, IEntityBase
{
public virtual byte[] RowVersion { get; protected set; }
public virtual DateTime? CreateDate { get; set; }
public virtual string CreateUser { get; set; }
public virtual DateTime? ModifyDate { get; set; }
public virtual string ModifyUser { get; set; }
}
Here is my context model configuration for the Degree entity:
internal class DegreeConfiguration : EntityTypeConfiguration<Degree>
{
internal DegreeConfiguration()
: base()
{
ToTable("Degree", "dbo");
Property(x => x.RowVersion).IsRowVersion();
}
}
Because of my application requirements, I must load the Person entity using the Include method to eagerly load the Degree entity so the object graph is
fully populated when the consumer requests the entity.
return ctx.People.Include(p => p.Degree).Where(x => x.PersonId == id).First();
When the DegreeId property of the Person object is modified and attached to the Context, the following Update statement is generated
upon calling SaveChanges():
exec sp_executesql N'declare #p int
update [dbo].[Degree]
set #p = 0
where (([DegreeId] = #0) and ([RowVersion] = #1))
select [RowVersion]
from [dbo].[Degree]
where ##ROWCOUNT > 0 and [DegreeId] = #0',N'#0 int,
#1 binary(8)',#0=2,#1=0x00000000000007DF
This is occurring even though I am not knowingly updating the Degree entity and causes havoc when two or more users using the application simultaneously.
To suppress the Update statement from being generated on the Degree navigation property, I commented out the concurrency check on the model configuration as such:
internal class DegreeConfiguration : EntityTypeConfiguration<Degree>
{
internal DegreeConfiguration()
: base()
{
ToTable("Degree", "dbo");
//Property(x => x.RowVersion).IsRowVersion();
}
}
Upon re-executing the process, EF no longer generates the problematic Update statement.
I've done a considerable number of searches both on MS site for EF 4.1, as well as general Google searches. I cannot come up with any concrete explanations.
Thank you.