Get list of select items from database and populate objects - entity-framework

I am trying to Populate an object inside another object while avoiding two things:
Getting all the items in table
Making a separate call to the db for each item
Here is my code
Classes
public class Profile: BaseEntity
{
public Picture Picture { get; set; }
public string Name { get; set; }
}
public partial class Picture : BaseEntity
{
public string Name {get; set;}
}
Services
public class ProfileService : IProfileService
{
private readonly IRepository<Profile> _profileRepo;
private readonly IRepository<Picture> _pictureRepo;
public ProfileService(IRepository<Profile> profileRepo, IRepository<Picture> pictureRepo)
{
_profileRepo = profileRepo;
_pictureRepo = pictureRepo;
}
public IEnumerable<Profile> GetProfiles()
{
IEnumerable<Profile> profiles = _profileRepo.Table.ToList();
// I am guessing I would retrieve pictures here based off of picture
// ids that are in the profiles
// than I would populate the profiles with the pictures
}
}
Also in the database PictureId is a foreign key in Profile table.

If there is a relation between the objects then you should be able to just create a method in your repository to do:
public IEnumerable<Profile> GetAllProfilesWithPictures()
{
return your_context.Profiles.Include(x => x.Picture);
}
This will return all the profiles, along with their picture.
Not quite sure on what you mean when you say Getting all items in table.. If you want to limit what you get then you can put a where clause or whatever on the above statement.

Related

EF Core Hierarchy TPH map two entities to one

I have a base project, which has some basic entities. Let's use BaseUser as one. In the child project, it references base project and User class inherits from that BaseUser. The only difference is the User class has List<Blogs>, so no additional properties, no change in mapping.
When I query Users, it does not find any because they are created as BaseUser and the query has discriminator value of "User". I don't want to query "BaseUser" because I want the relational property of List<Blogs>.
Is there any way to tell EF to basically treat these classes as one? Is there a better way to handle the split? (obviously base project has no concept of blogs, so cannot move the List to the base)
Some sample classes as requested
/* Base Project (nuget package created) */
public class BaseUser {
public int UserId { get; set; }
public int Name { get; set; }
}
public class BaseContext : DbContext {
public DbSet<BaseUser> BaseUsers {get;set;}
}
public class BaseDataInstaller {
BaseContext _ctx;
public BaseDataInstaller( BaseContext ctx ){
_ctx = ctx;
}
public void Install(){
_ctx.BaseUsers.Add( new BaseUser { Name="Demo User 1" } );
_ctx.BaseUsers.Add( new BaseUser { Name="Demo User 2" } );
_ctx.SaveChanges();
}
}
/* Child Project (consumes nuget package)*/
public class User : BaseUser {
List<Blogs> Blogs { get; set; }
}
public class ProjectContext : BaseContext {
public DbSet<User> Users { get; set; }
}
public class SomeService {
ProjectContext _ctx;
public BaseDataInstaller(ProjectContext ctx){
_ctx = ctx;
}
//Finds 0 users
public void PrintUsers(){
var users = _ctx.Users.ToList();
users.ForEach( u=> Console.WriteLine(u.Name) );
}
//Finds Users
public void PrintBaseUsers(){
var users = _ctx.BaseUsers.ToList();
users.ForEach( u=> Console.WriteLine(u.Name) );
}
}
Comparing the SQL generated, there is a discriminator added
where Discriminator = 'BaseUser' or where Discriminator = 'User'
There are no properties which are different between the two, just the relationship with the blogs.
So is there a way to either make both have the same Discriminator value or another way to solve this?
UPDATE 1
The discriminator only appears if the DbContext knows about BOTH entities. if it only knows about the one, it is happy to map onto the table. Even if the child inherits from the base, it still doesn't need a discriminator. So I think the challenge is to re-work the base so the context doesn't know about the base. This does feel like a workaround though. Maybe the structure should change:
instead of User : BaseUser use a property
User
- int ChildUserId {get; set;}
- BaseUser BaseUser {get; set;}
- SomeObject SomeNavProperty etc
it will mean a new table for each inherited project, but would allow the project to add it's own specific data too...

Related data not being added for existing parent entity

Im trying to save a rating against a place, I have the code below, but it doesnt seems to save rating (to the ratings table) for an existing entity
place.Ratings.Add(rating);
_placeRepository.AddPlaceIfItDoesntExist(place);
_placeRepository.Save();
This is the repository method
public void AddPlaceIfItDoesntExist(Place place)
{
var placeItem = context.Places.FirstOrDefault(x => x.GooglePlaceId == place.GooglePlaceId);
if(placeItem==null)
{
context.Places.Add(place);
}
else
{
context.Entry(placeItem).State = EntityState.Modified;
}
}
and this is the poco
public class Place
{
public Place()
{
Ratings = new List<Rating>();
}
public int Id { get; set; }
public string Name { get; set; }
public string GooglePlaceId { get; set; }
}
I think the crux of the problem is because i need to check if the place exists based on googleplaceid(a string) rather than the id (both are unique per place btw)
Here
context.Entry(placeItem).State = EntityState.Modified;
you just mark the existing placeItem object as modified. But it's a different instance than the passed place object, hence contains the orginal values.
Instead, replace that line with:
context.Entry(placeItem).CurrentValues.SetValues(place);
Alternatively, you can use the DbSetMigrationsExtensions.AddOrUpdate method overload that allows you to pass a custom identification expression:
using System.Data.Entity.Migrations;
public void AddPlaceIfItDoesntExist(Place place)
{
context.Places.AddOrUpdate(p => p.GooglePlaceId, place);
}

Can't Get EF 6 Code First To Create the Tables

I already have a database with tables outside EF scope. But I want that the tables which will be used by EF to be created automatically.
public class SessionInfo
{
public Guid Id {get;set;}
public string Name { get; set; }
public DateTime StartsOn { get; set; }
public DateTime EndsOn { get; set; }
public string Notes { get; set; }
}
public class StudentsDbContext:DbContext
{
public StudentsDbContext():base("name=memory")
{
Database.Log = s => this.LogDebug(s);
}
public DbSet<SessionInfo> Sessions { get; set; }
}
This code just throws an exception because the table SessionInfoes doesn't exist.
using (var db = new StudentsDbContext())
{
db.Sessions.Add(new SessionInfo() {Id = Guid.NewGuid(), Name = "bla"});
var st = db.Sessions.FirstOrDefault();
}
What do I need to do so that EF will create the "SessionInfoes" (whatever name, it's not important) table by itself? I was under the impression that Ef will create the tables when the context is first used for a change or a query.
Update
After some digging, it seems that EF and Sqlite don't play very nice together i.e at most you can use EF to do queries but that's it. No table creation, no adding entities.
EF needs additional information in order to do this. You'll have to specify an IDatabaseInitializer first. Take a look at this list and find one that is appropriate for your needs (for example: MigrateDatabaseToLatestVersion, DropCreateDatabaseAlways, DropCreateDatabaseIfModelChanges, etc).
Then create your class:
public class MyDatabaseInitializer : MigrateDatabaseToLatestVersion
<MyDbContext,
MyDatabaseMigrationConfiguration>
Then also create the configuration for the initializer (ugh right?):
public class DatabaseMigrationsConfiguration
: DbMigrationsConfiguration<MyDbContext>
{
public DatabaseMigrationsConfiguration()
{
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = true;
}
protected override void Seed(MyDbContext context)
{
// Need data automagically added/update to the DB
// during initialization?
base.Seed(context);
}
}
Then one way to initialize the database is:
var myContext = new MyDbContext(/*connectionString*/);
Database.SetInitializer<MyDbContext>(new MyDatabaseInitializer());
myContext.Database.Initialize(true);
Some people prefer the to use the command line to migrate databases, but I don't want to assume I'll always have access to the database from a command lin.

Duplicate Record in one to one relationship when savechange

i have a one to one relationship between two entity and whn i want set navigation property add duplication record tu my table
because my English is poor, i attach my Project for you Here
tnx
my code here:
here my Entity:
public partial class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public int UserType { get; set; }
}
public partial class Storage : User
{
public virtual Store Store { get; set; }
}
public partial class Store
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Storage Storage { get; set; }
}
here my Add Store botton:
User u = (User)(comboBox1.SelectedItem);
Storage st = new Storage(u);
Store s = new Store(textBoxStorename.Text);
s.SetStorage(st);
s.Save(st);
and here my Store Class:
public partial class Store
{
public Store()
{
}
public Store(string name)
{
this.Name = name;
}
public void SetStorage(Storage s)
{
if (s != null)
{
this.Storage = s;
}
}
public void Save(Storage s)
{
using (var storekeeper = new TestContainer())
{
bool flag = false;
foreach (var item in storekeeper.Stores)
{
if (item.Equals(this))
{
flag = true;
}
}
if (flag)
{
MessageBox.Show("Duplicat Error", "", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
storekeeper.Users.Attach(s);
storekeeper.Stores.Add(this);
storekeeper.SaveChanges();
}
catch (Exception e )
{
MessageBox.Show(e.Message);
}
}
}
public override string ToString()
{
return this.Name;
}
}
The real problem here is your data model.
In any case, what's happening is you're recreating the User entity that already exists when you instantiate the new Storage instance. Storage inherits from User, so storage is an instance of user conceptually. The storage model, however, is different - Storage is a relationship table between User and Store.
By instantiating a new instance of Storage, copying the existing User object's properties to the base properties of the Storage instance, then simply attaching the Storage object to the Users DbSet, entity framework rightly thinks that the object is new and needs to be inserted - including the User entity object. The user record already exists, hence your duplicate key problem.
Solution: change your data model. No reason to have the 1..1 relationship entity "Storage". Simply create a nullable Store property in the User entity. If you want to enforce that an instance of Store can only be referenced by a single User, then the Store entity should either use the User.Id property as its primary key (with the FK relationship) or have a FK UserId property in Store that must be unique.

How to create a specific class for a collection in EF?

I want to create a specific class to manage a collegion in my application.
For example, I have a Store and I have a list of customers in a collection, in this collection I have a customer that is the customer of the month, and some customers that got a prize to get a discount for some reason. Let's get to the code:
public class Store {
public ICollection<Customer> Customers { get; set; }
public Customer CustomerOfTheMonth
{
//get and set for customer of the month
}
public ICollection<Customer> DiscountCustomers
{
//get and set for customer of the month
}
public ICollection<Customer> GetAllCustomers
{
//get and set for customer of the month
}
}
But in my database, I only have two tables. Store, and Customer.
What I want to do is create a specific collection for the customer, to remove the logic from the Store and put in a specific class, after all, I don't feel that the logic belongs to neither of those classes.
I wans tomething like this:
public class Store {
internal CustomerCollection Customers { get; set; }
//get and set for the propertis, just delegating for the collection
}
public class CustomerCollection {
public ICollection<Customer> Customers { get; set; }
public ICollection<Customer> DiscountCustomers
{
//get and set for customer of the month
}
//get and set with logic to filter the collection
}
Is there away to create this mapping and keep with only two tables in the database? I want to make it transparent to the application. Sorry for the code problems, typed in stack overflow and didn't check the syntax.
don't need to create your business logic to your model classes. Separate your logic to upper level. Here are your model classes. It will create your relationships as you want
public class Store {
public vertual ICollection<Customer> Customers { get; set; }
//get and set for other propertis
}
public class Customer{
//get and set for other propertis
}
Create a repository or service layer to apply specific business logic ,
public ICollection<Customer> GetDiscountCustomers()
{
return dbContext.Customers.where(c=>c.discount=true).ToList()
}
if you want to load stores with customers at once you can use eager loading,
public ICollection<Store> GetAllStores()
{
return dbContext.Stores.Include("Customers").ToList()
}