Entity Framework add local data to list from database - c#

I am pretty new to Entity Framework and I am using this method in order to query through my database:
var _context = new StudioEntities();
var results = _context.tblStudios.Select(u => new
{
u.Standort,
u.Name,
u.Id
}).ToList();
Now my goal is to add local data as well which isn't present in the database. I tried it with this code but it didn't work:
results.Add(new tblStudio { Id = 0, Name = "Gesamt" });
Can someone help me with this? Thanks
Edit:
My table class looks like this:
public partial class tblStudio
{
public int Id { get; set; }
public string Name { get; set; }
public string Standort { get; set; }
public Nullable<int> Plz { get; set; }
}

The result is not a List of tblStudios, it is a List of Anonymous Type. So if you want to add an item to the result you should do like this:
var results = _context.tblStudios.Select(u => new tblStudiosDTO()
{
Standort = u.Standort,
Name = u.Name,
Id = u.Id
}).ToList();
results.Add(new tblStudiosDTO() { Id = "0", Name = "Gesamt" });
But because you cannot project onto a mapped entity then you need to create a DTO class like tblStudiosDTO with needed properties from the tblStudios entity.
public class tblStudiosDTO
{
public string Standort { get; set; }
public string Name { get; set; }
public string Id { get; set; }
}

Related

c# LINQ query to select whole object into new?

Not sure if i worded the question correctly, but what im trying to do is return a new viewmodel with one of the parts being a booking:
public class Booking
{
public int BookingId { get; set; }
public int CustomerId { get; set; }
public Guid UniqueId { get; set; }
public string EventId { get; set; }
public bool IsPaid { get; set; }
public double Price { get; set; }
public DateTime BookingDate { get; set; }
public DateTime DateBooked { get; set; }
[JsonIgnore]
public Customer Customer { get; set; }
[JsonIgnore]
public ICollection<BookingService> BookingServices { get; set; }
[NotMapped]
public IEnumerable<Service> Services { get; set; }
}
and my query is:
var customers = _dbContext.Customers
.Select(c => new CustomerBookingsViewModel
{
Customer = c,
Bookings = c.Bookings.Select(b => new Booking
{
BookingId = b.BookingId,
BookingDate = b.BookingDate,
DateBooked = b.DateBooked,
CustomerId = b.CustomerId,
UniqueId = b.UniqueId,
EventId = b.EventId,
IsPaid = b.IsPaid,
Price = b.Price,
Services = b.BookingServices.Select(s => s.Service)
}),
}
)
.ToList();
What I want to know is how to I select all the booking info into the booking without selecting each part, ie:
BookingId = b.BookingId,
BookingDate = b.BookingDate,
DateBooked = b.DateBooked,
CustomerId = b.CustomerId,
UniqueId = b.UniqueId,
EventId = b.EventId,
IsPaid = b.IsPaid,
Price = b.Price,
Can it be done or because the list of services is inside the booking model it cant?
Thanks.
You could implement the IClonable interface on your class.
public class MyClass : ICloneable
{
public int Id { get; set; }
public object Clone() => MemberwiseClone();
}
Usage:
var list1 = new List<MyClass>
{
new MyClass() { Id = 2 },
new MyClass() { Id = 5 }
};
var list2 = list1.Select(x => (MyClass)x.Clone()).ToList();
list2.First().Id = 10; //list1 won't be affected
You should use AutoMapper here to avoid writing each path.
https://automapper.org/
http://docs.automapper.org/en/stable/Getting-started.html
There is no other way, at least it is not related to LINQ or queries.
The question "How to clone an object" has been answered here:
Creating a copy of an object in C#
There is no LINQ way to do this. I would suggest using custom Attribute marking every property you want to copy. This would help if you want not to copy the whole object but some properties. After marking every property you need you can just set the marked props with reflection from one of the objects to the other.

Join to list in LINQ to SQL (SQL mode)

I have the table News:
public class New
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Title { get; set; }
}
and every new has a poll, and it's saved in the table NewOptions:
public class NewOption
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("New")]
public int NewId { get; set; }
[Required]
public string Option { get; set; }
public virtual New New { get; set; }
}
News 1:M NewOptions
And their respective DTOs. I want get the latest news with their respective options:
var news =
from theNew in this.context.News
join option in this.context.NewOptions on theNew.Id equals option.NewId
select new NewDto
{
Id = theNew.Id,
Title = theNew.Title,
Options = new NewOptionDto
{
Id = option.Id,
Option = option.Option
}
};
The error is: Can't convert type NewOptionDto to IEnumerable
Try code like this. I'm not sure if I got it 100%, but it is close. Your New class needs a property for NewOption for code below to work.
var news =
(from theNew in this.context.News
join option in this.context.NewOptions on theNew.Id equals option.NewId
select new {theNew = theNew, option = option}).Select(x => new NewDto() {
Id = x.theNew.Id,
Title = x.theNew.Title,
Options = x.option.Select(y => new NewOptionDto() {
Id = y.Id,
Option = y.Option
}).ToList()
}).ToList();

How to update entity with IntersectionTable in c# ef5 db first approach?

I created my database and started developing a web application in c# with EF5 and the DB First approach. I can modify my entities on their own data fields but donĀ“t get it to work when it comes to updating relationships. A simple relationship example is Project <- ProjectCategoryIntersection -> Category
Model:
public class Project
{
public TProject project { get; private set; }
public List<string> Categories { get; set; }
}
public partial class TProject //generated table object
{
public virtual ICollection<TProjectCategoryIntersection> TProjectCategoryIntersection { get; set; }
}
public partial class TProjectCategoryIntersection
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int ProjectCategoryId { get; set; }
public virtual TProject T_Project { get; set; }
public virtual TCategory T_ProjectCategory { get; set; }
}
Save:
public void SaveProject(Project project)
{
var context = new ProjectManagementEntities();
TProject projectToUpdate = new TProject();
projectToUpdate.Id = project.Id;
foreach (var category in project.Categories)
{
var cat = (from c in context.TProjectCategory
where c.Name == category
select c).FirstOrDefault();
var inters = new TProjectCategoryIntersection() { ProjectCategoryId = cat.Id, ProjectId = project.project.Id, TProject = project.project, TProjectCategory = cat };
projectToUpdate.TProjectCategoryIntersection.Add(inters);
}
var entry = context.Entry(projectToUpdate).State = EntityState.Modified; //throws exceptions
context.SaveChanges();
}
exception:
Conflicting changes to the role 'TProject' of the relationship 'ProjectManagementModel.FK_TProjectCategoryIntersection_TProject' have been detected.
I also receive a multiple instances ChangeTracker exception when i try to add the categories directly to the project object:
project.project.TProjectCategoryIntersection.Add(inters);
Should i remove the generated table object from my model?
public class Project
{
public TProject project { get; private set; } //remove this?
public List<string> Categories { get; set; }
}
Solution
I ended up removing the generated table object public TProject project { get; private set; } and changed my code to:
public void SaveProject(Project project)
{
var context = new ProjectManagementEntities();
var projectToUpdate = context.T_Project.Find(project.Id);
foreach (var item in projectToUpdate.T_ProjectCategoryIntersection.ToList())
{
var oldCat = context.T_ProjectCategoryIntersection.Find(item.Id);
context.T_ProjectCategoryIntersection.Remove(oldCat);
}
foreach (var category in project.Categories)
{
var cat = (from c in context.T_ProjectCategory
where c.Name == category
select c).FirstOrDefault();
var inters = new T_ProjectCategoryIntersection() { ProjectCategoryId = cat.Id, ProjectId = project.Id };
context.T_ProjectCategoryIntersection.Add(inters);
}
//more code...
context.Entry(projectToUpdate).State = EntityState.Modified;
context.SaveChanges();
}
Apperantly this happens when you use reference to an object and also an Integer for the ID within the same object and change both of them. When this happens EF can not know which one is the correct reference
Try setting only Ids and set null for references like
var inters = new TProjectCategoryIntersection() { ProjectCategoryId = cat.Id,
ProjectId = project.project.Id};

There has to be a better way to add these using LINQ, right?

I am new to LINQ and and come up with the below to add new information to my DB using LINQ and EF5 but I am sure there is a more efficant, better, way to do this I just don't know it. I was hoping to get some input on what I can do to acceive the same but with less/more efficant code.
using (var db = new FullContext())
{
if (ddlItemType.SelectedValue == "Other")
{
var NewItemType = new ItemType { Name = tbNewType.Text };
db.ItemTypes.Add(NewItemType);
db.SaveChanges();
}
if (ddlRegion.SelectedValue == "Other")
{
var NewRegion = new ReleaseRegion { Name = tbNewRegion.Text };
db.Regions.Add(NewRegion);
db.SaveChanges();
}
var NewItemTypeID = byte.Parse((from i in db.ItemTypes
where i.Name == tbNewType.Text
select new { i.ID }).ToString());
var NewRegionID = byte.Parse((from r in db.Regions
where r.Name == tbNewRegion.Text
select new { r.ID }).ToString());
var NewItem = new Item
{
Name = tbItemName.Text,
TypeID = NewItemTypeID,
RegionID = NewRegionID,
Condition = ddlCondition.SelectedValue.ToString(),
UPC = tbUPC.Text,
ISBN = tbISBN.Text,
IsColleciton = cbIsCollection.Checked,
CollectionID = Convert.ToInt16(ddlCollection.SelectedValue),
Notes = tbNotes.Text
};
db.Items.Add(NewItem);
db.SaveChanges();
}
Item.cs:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace FFCollection.DAL
{
[Table("Items")]
public class Item
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int16 ID { get; set; }
[Required]
public string Name { get; set; }
public byte TypeID { get; set; }
[ForeignKey("TypeID")]
public virtual ItemType Type { get; set; }
public byte RegionID { get; set; }
[ForeignKey("RegionID")]
public virtual ReleaseRegion Region { get; set; }
[Required]
public string Condition { get; set; }
public string UPC { get; set; }
public string ISBN { get; set; }
public string Notes { get; set; }
[Required]
public Boolean IsColleciton { get; set; }
public Int16 CollectionID { get; set; }
[ForeignKey("CollectionID")]
public virtual Item InCollectionID { get; set; }
}
}
ItemType.cs:
using System.ComponentModel.DataAnnotations.Schema;
namespace FFCollection.DAL
{
public class ItemType
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public byte ID { get; set; }
public string Name { get; set; }
}
}
The databinding to DDL:
using (var db = new FullContext())
{
ddlItemType.DataSource = (from t in db.ItemTypes
select new { t.ID, t.Name }).ToList();
ddlItemType.DataTextField = "Name";
ddlItemType.DataValueField = "ID";
ddlItemType.DataBind();
ddlItemType.Items.Insert(0, new ListItem("Other", "Other"));
}
Part of the trouble isn't Linq, it's how you're using EF. Based on that example code you're using it as a data layer wrapper rather than an ORM. When constructing an object graph you should deal with the objects where you can, not foreign key IDs. The power of an ORM is that you can deal specifically with object graphs that are mapped to data, so that when you tell the ORM to save an object (and it's associated relatives) the ORM takes out all of the work of inserting/updating new records and wiring up keys. You're doing all that extra work in code, where an ORM like EF should allow you to accomplish what you want with a handful of lines.
For a start, when dealing with combo boxes, bind them to a data structure that includes the lookup value's ID that you can resolve instances of existing ItemTypes or Regions to associate with your new Item. (or in the case of selections of "other".
What I'd be looking at would be to bind the combo boxes to ItemType/Regions with the "Other" being a specific place-holder that the code will substitute with a new object if selected based on entries in the text fields. Then rather than saving the new objects before appending to the "Item", you simply set the references and save the Item which should cascade insert operations for the new lookup objects.
After this code executes EF will automatically put an ID into your NewItemType entity. You don't need to go and find it again, you could just say NewItemType.ID. This will only work after you have already called db.SaveChanges().
if (ddlItemType.SelectedValue == "Other")
{
var NewItemType = new ItemType { Name = tbNewType.Text };
db.ItemTypes.Add(NewItemType);
db.SaveChanges();
}

Can I create nested classes when using Linq-To-Entities?

I'm still learning Entity Framework and Linq-To-Entities, and I was wondering if a statement of this kind is possible:
using (var context = new MyEntities())
{
return (
from a in context.ModelSetA.Include("ModelB")
join c in context.ModelSetC on a.Id equals c.Id
join d in context.ModelSetD on a.Id equals d.Id
select new MyModelA()
{
Id = a.Id,
Name = a.Name,
ModelB = new MyModelB() { Id = a.ModelB.Id, Name = a.ModelB..Name },
ModelC = new MyModelC() { Id = c.Id, Name = c.Name },
ModelD = new MyModelD() { Id = d.Id, Name = d.Name }
}).FirstOrDefault();
}
I have to work with a pre-existing database structure, which is not very optimized, so I am unable to generate EF models without a lot of extra work. I thought it would be easy to simply create my own Models and map the data to them, but I keep getting the following error:
Unable to create a constant value of type 'MyNamespace.MyModelB'. Only
primitive types ('such as Int32, String, and Guid') are supported in
this context.
If I remove the mapping for ModelB, ModelC, and ModelD it runs correctly. Am I unable to create new nested classes with Linq-To-Entities? Or am I just writing this the wrong way?
What you have will work fine with POCOs (e.g., view models). Here's an example. You just can't construct entities this way.
Also, join is generally inappropriate for a L2E query. Use the entity navigation properties instead.
I have created your model (how I understand it) with EF 4.1 in a console application:
If you want to test it, add reference to EntityFramework.dll and paste the following into Program.cs (EF 4.1 creates DB automatically if you have SQL Server Express installed):
using System.Linq;
using System.Data.Entity;
namespace EFNestedProjection
{
// Entities
public class ModelA
{
public int Id { get; set; }
public string Name { get; set; }
public ModelB ModelB { get; set; }
}
public class ModelB
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ModelC
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ModelD
{
public int Id { get; set; }
public string Name { get; set; }
}
// Context
public class MyContext : DbContext
{
public DbSet<ModelA> ModelSetA { get; set; }
public DbSet<ModelB> ModelSetB { get; set; }
public DbSet<ModelC> ModelSetC { get; set; }
public DbSet<ModelD> ModelSetD { get; set; }
}
// ViewModels for projections, not entities
public class MyModelA
{
public int Id { get; set; }
public string Name { get; set; }
public MyModelB ModelB { get; set; }
public MyModelC ModelC { get; set; }
public MyModelD ModelD { get; set; }
}
public class MyModelB
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyModelC
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyModelD
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
// Create some entities in DB
using (var ctx = new MyContext())
{
var modelA = new ModelA { Name = "ModelA" };
var modelB = new ModelB { Name = "ModelB" };
var modelC = new ModelC { Name = "ModelC" };
var modelD = new ModelD { Name = "ModelD" };
modelA.ModelB = modelB;
ctx.ModelSetA.Add(modelA);
ctx.ModelSetB.Add(modelB);
ctx.ModelSetC.Add(modelC);
ctx.ModelSetD.Add(modelD);
ctx.SaveChanges();
}
// Run query
using (var ctx = new MyContext())
{
var result = (
from a in ctx.ModelSetA.Include("ModelB")
join c in ctx.ModelSetC on a.Id equals c.Id
join d in ctx.ModelSetD on a.Id equals d.Id
select new MyModelA()
{
Id = a.Id,
Name = a.Name,
ModelB = new MyModelB() {
Id = a.ModelB.Id, Name = a.ModelB.Name },
ModelC = new MyModelC() {
Id = c.Id, Name = c.Name },
ModelD = new MyModelD() {
Id = d.Id, Name = d.Name }
}).FirstOrDefault();
// No exception here
}
}
}
}
This works without problems. (I have also recreated the model from the database (which EF 4.1 had created) in EF 4.0: It works as well. Not surprising since EF 4.1 doesn't change anything in LINQ to Entities.)
Now the question is why you get an exception? My guess is that there is some important difference in your Models or ViewModels or your query compared to the simple model above which is not visible in your code example in the question.
But the general result is: Projections into nested (non-entity) classes work. (I'm using it in many situations, even with nested collections.) Answer to your question title is: Yes.
What Craig posted does not seem to work for nested entities. Craig, if I am misunderstood what you posted, please correct me.
Here is the workaround I came up with that does work:
using (var context = new MyEntities())
{
var x = (
from a in context.ModelSetA.Include("ModelB")
join c in context.ModelSetC on a.Id equals c.Id
join d in context.ModelSetD on a.Id equals d.Id
select new { a, b, c }).FirstOrDefault();
if (x == null)
return null;
return new MyModelA()
{
Id = x.a.Id,
Name = x.a.Name,
ModelB = new MyModelB() { Id = x.a.ModelB.Id, Name = x.a.ModelB..Name },
ModelC = new MyModelC() { Id = x.c.Id, Name = x.c.Name },
ModelD = new MyModelD() { Id = x.d.Id, Name = x.d.Name }
};
}
Since Entity Framework can't handle creating nested classes from within the query, I simply returned an anonymous object from my query containing the data I wanted, then mapped it to the Model

Categories

Resources