Adding records in multiple table using EF6 code first - c#

I am using EF6 code first approach, i have 3 tables:
ProductPromotionalOffersPromotionalOfferProduct
I have few products already added inside the products table, now i want that on adding new promotional offer, i should be able to link some products (more than 1) to promotionalofferproducts, but in my case it adds new product
here are my Entities
Product
public class Product
{
[Key]
public long ProductId { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
}
PromotionalOffer
public class PromotionalOffer
{
[Key]
public long PromotionalOfferID { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
public virtual List<Product> Products { get; set; }
public void Add()
{
db.PromotionalOffers.Add(this);
db.SaveChanges();
}
}
The 3rd table was automatically created by EF based on List<Product> products in PromotionalOffer class.
And below is my client code:
IPromotionalOffer pOffer = Factory.Instance.Create<IPromotionalOffer>();
Domain.Product p = new Domain.Product
{
ProductId = 1,
Name = "Colgate",
};
pOffer.Name = "Holi";
pOffer.Products.Add(p);
pOffer.Add();
Though this adds entry in PromotionalOfferProducts it also creates entry in Products table (though it should not add in Product table).

The Problem is you are adding a new product
because you create a new instance of Product, instead of using an existing one.
So to solve this, Load your product with entityframework, instead of instantiating your own instance of product.
so in general entityframework terms if you did not use dependecy injection container something like this:
var loadedProduct = dbContext.Product.Find(yourProductId);
IPromotionalOffer pOffer = Factory.Instance.Create<IPromotionalOffer>();
pOffer.Name = "Holi";
pOffer.Products.Add(loadedProduct);
pOffer.Add();
the reason is, your entity classes is not actually used by entityframework directly, entityframework inherits from your product class and make its own class that it can add more properties to, in order to track the entity. If you create a new instance yourself, entityframework cant track it, and thus assumes its a new product, instead of an existing one, since it is tracking the existing ones that are being loaded.
More ideally i think you need to change your entity mappings so that you can do this the other way around, thus load your product from entity framework, and then add a promotionalOffer to your product, instead of adding a product to your promotional offer.

Related

Is it possible to select mapped entity into another mapped entity in a single query?

Is there any elegant way to perform a nested LINQ selection into objects that are being selected themselves? In other words, let's assume there are three DB tables all with one-to-many relation: Schedule, Day (One schedule may have many days) and Activity (One day may have many activities. I would like to try to build a query which selects data into related mapped objects without the necessity of creating additional helper objects. Is that even possible? Please see below objects which map DB tables:
public Class Schedule{
public int ScheduleId { get; set; }
...
public byte[] RowVersion { get; set; }
[NotMapped]
public List<Day> days; //Supposed to store Day objects
}
public Class Day{
public int DayId { get; set; }
...
public byte[] RowVersion { get; set; }
[NotMapped]
public List<Activity> activities; //Supposed to store Activity objects
}
public Class Activity{
public int ActivityId { get; set; }
...
public byte[] RowVersion { get; set; }
}
At this point I use additional classes to fetch data: SchedulePrim, DaysPrim and ActivitiesPrim. After the query is executed I put Prims into [NotMapped] attributes of proper objects (see above) and then get rid of Prims. To me this seem like using unnecessary resources. The query looks somewhat like this:
from schedules in context.Schedules.Where(...)
select new SchedulePrim
{
Schedule = schedules
DaysPrim = from days in context.Days.Where(...)
select new DaysPrim
{
Day = days
ActivitiesPrim = from activities in context.Activities.Where(...)
select new DaysPrim
{
Activity = activities
}
}
}
Here comes the logic of reprocessing fetched data into proper entities.
Is there a faster way to do this? The way that lets selecting data into [NotMapped] attributes on the fly, without the need of introducing additional processing?
Just eliminate the NotMapped attributes and the "additional classes", make sure you have proper foreign keys, and load the data using Include.
db.Schedules.Include(s => s.Days).ThenInclude(d => d.Activities).Where(...)

Data Grid View Binding Complex Type using code first entity framework

We are using Code First Entity Framework in Desktop Application, And we created one to zero relationship like Company -> Product, Package Type -> Product, Product Type -> Product. So we have created all the master form for Company, Package Type, Product Type, but when we are going to bind data for company etc then it will show error because we have make relationship with company and product. Our entity like that
public class Company : Common
{
public int CompanyID { get; set; }
public string CompanyName { get; set; }
public virtual Product Products { get; set; }
}
public List<Company> Get(string search)
{
using (EF6CodeFirstContext db = new EF6CodeFirstContext())
{
if (string.IsNullOrEmpty(search))
return db.Companies.ToList();
else
return db.Companies.Local.Where(x => x.CompanyName.ToLower().Contains(search)).ToList();
}
}
Datagridview1.DataSource = Obj.Get(strSearch);
So, Please help me if there is any way to bind direct datagridview without using any type of loop.

Refresh EF 6 DBContext

I am working with Entity Framework 6 and I have an product object that has a list of variants like so:
public class Product
{
public int ProductId { get; set;}
public virtual ICollection<Variant> Variants { get; set;}
... other properties
}
public class Variant
{
public int VariantId { get; set;}
public int ProductId { get; set;}
public virtual Product Product { get; set;}
public int StoreId { get; set;}
... other properties
}
And I use this to get the products from the context:
public static GetProducts()
{
using (MyDBContext context = new MyDBContext())
{
return context.Products.Include(p => p.Variants);
}
}
Now this all works fine and when I get the Product it comes back with the variants. However, this morning I foolishly used the Product from the context instead of a DTO and filtered the variants based on a StoreId and now whenever I get a Product, it only returns the variants for that store (even though I never committed any changes).
I have checked the db and all the variants are still there so how do I reset my context so that I get all variants again.
I have tried the following:
resetting iis
cleaning and rebuilding the solution
changing the object to return an extra property
reloading the product using:
foreach (Product product in context.Products)
{
context.Entry(product).Reload();
}
But nothing seems to work, is there anything else I need to do to reset the context?
As it turns out it was a config error with the variant entity rather than the filtered context. I was only using the VariantID as the Key but as there were multiple variants with the same id, it was overriding the following variants with the same values as the first one with that id when it was mapping the objects back after the query (making it look as if there was only the store I had filtered).
I fixed this by making the key unique to the store and the variant id:
HasKey(v => new {v.VariantId, c.StoreId});

Is it possible to add Properties to an Entity Context and therefore it's connected database table

I have fields that the customer can add or remove in a list on their application and need to therefor add them to the database / context for later use.
For example I have a model first entity context dbContext and the model in question is Customers with properties id, name, accesslevel, ...
What i want to know is if there is a way for me to programmatically add a property to the Entity Model / or context or if I need to take an entirely different approach to dynamic database fields?
Ex: Needing to add a Inactive boolean field to it's Model / and Database table.
No, that's definitely not possible. EF doesn't support it and even if you could, adding new properties to your models would require a database migration every time. If you want the user to be able to add custom data to the Cutomers class, consider something like this:
public class Customers
{
public int Id { get; set; }
public string Name { get; set; }
//... more properties.
public virtual ICollection<UserData> UserData { get; set; }
}
public class UserData
{
public string PropertyName { get; set; }
public string PropertyValue { get; set; }
}
Otherwise, the only way you could do something like this is with a NoSQL database like MongoDB.

How do I save an entity with NHibernate that is composed of other complex types, but I only have the ID of the other complex types

I'm working on my first NHibernate project, so this may seem to be a simple question.
The below is simplified from my real scenario to convey the specific question.
Lets say I have a Customer entity
public class Customer
{
prop virtual int ID { get; set; }
prop virtual string Name { get; set; }
prop virtual Region Region { get; set; }
}
and my Region Entity (regions are like, NorthWest, MidWest, etc - a pretty defined list that would be in some sort of drop-down)
public class Region
{
prop virtual int ID { get; set; }
prop virtual string Name { get; set; }
private readonly IList<Customer> _customers = new List<Customer>();
public virtual void Add(Customer customer)
{
_customers.Add(customer);
}
public virtual void Remove(Customer customer)
{
_customers.Remove(customer);
}
public virtual Customer[] GetCustomers()
{
return _customers.ToArray();
}
}
When I go to Persist a Customer Entity, I really only want to have 3 pieces of information (Customer.ID, Customer.Name, & Customer.Region.ID), how do I accomplish this, because NHibernate expects a Customer entity that includes a full Region entity (not just the ID)...
You can use Load to get the Region by ID without hitting the database. This will give you an entity that NHibernate will recognise and allow you to persist with without the extra database call.
Reference
I think if you already have the ID of the Region, you just have to assign that Id to a new Region object and attach it to your new Customer. As long as you don't have the cascade attribute for that association in the Customer mapping set to "all" or "update" (which would in fact update the data of the existing Region in the DB), it will attach the given Region to the new customer.

Categories