poco classes setting default value from another entity - c#

public class Price
{
public int Id { get; set; }
public decimal Price { get; set; }
}
public class Product
{
public Product()
{
// Set default value here
}
public int Id { get; set; }
public string Sku { get; set; }
public int PriceId { get; private set; }
public virtual Price Price { get; set; }
}
I've searched in the internet the best way to set a default value in a field. They say put in the constructor or make a backing field. Now what if I want to set a default value in an entity from another entity's field value? Say, the default value of Product's Price is the latest Price(in the Price class)
How do you achieve that?
Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id)?
Any help would be much appreciated. Thanks

What i would do is when i query the DB to return the Product / Products, i would let it fill the price for me.
This is a just for demonstration purposes
I would do something like:
public class MyDataAccessLayer
{
public IEnumerable<Product> GetProducts()
{
return DbContext.Products.Select(x => new Product
{
Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id)
};
}
public Product GetProduct(string id)
{
var product = DbContext.Products.FirstOrDefault(x => x.Id == id);
if (product != null)
{
product.Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id);
}
return product;
}
}
I would definitely let me Data Access Layer do the fetching for me and have it fill the latest price from the DB, and not have the POCO access my database.

Keep last inserted Producte in Cache. while you are creating new Product get last Product from Cache and set default values inside constructor and when you are inserting new Product update Cache.
While you are inserting new Product it's same as getting last Product from database because new inserted product is last product in database. So instead of:
var lastProduct = Context.Products.FirstOrDefault().OrderByDescending(c => c.Id);
do this:
var lastProduct = Context.Products.Add(newProduct);
Cache.Set("LastProduct", lastProduct, new CacheItemPolicy { SlidingExpiration = new TimeSpan(1, 0, 0, 0)});
There are some advantages here. first you dot query database every time for getting last product. second your are not going access EF Context inside your Poco class.
public class Product
{
public Product()
{
// var lastProduct = (Product)Cache["LastProduct"];
// Set default value here
}
public int Id { get; set; }
public string Sku { get; set; }
public int PriceId { get; private set; }
public virtual Price Price { get; set; }
}

Related

Entity framework cannot seem to create relationship

The entity framework isn't super new to me, however is confusing as I continue to expand my data models. I am attempting to create a class that has an array of another class. Class A or County.cs has a list of Class B or Product.cs
I cannot seem to create write these classes in a way that when you ask for context.counties you also get the array of products attached to it.
Class A or County.cs
public class County
{
[Key]
public int ID { get; set; }
public String Name { get; set; }
public List<Product> Products { get; set; } = new List<Product>();
[NotMapped]
public DateTime firstAppearance {
get {
var data = (from obj in Products orderby obj.Date descending select obj.Date).FirstOrDefault();
if (this.softwareIn)
{
return data;
}
else
{
var date = new DateTime(1,1,1);
return date;
}
}
set {
this.firstAppearance = value;
}
}
[NotMapped]
public bool softwareIn {
get {
return Products.Count() >= 1;
}
set {
this.softwareIn = value;
}
}
}
Class B or Product.cs
public class Product
{
[Key]
public int ID { get; set; }
public String Name { get; set; }
public DateTime Date { get; set; }
[NotMapped]
public DateTime DateUtc {
get {
return getUtcDate();
}
set {
this.DateUtc = value;
}
}
public DateTime getUtcDate() {
return this.Date.ToUniversalTime();
}
}
I just don't understand and haven't created enough of 1:M relations in the entity framework. Why cannot I do something like this and have it work all the time? The first time I run this I get the type of data I expect, the xx county has a product. However if I remove all this and just return the context.counties I get nothing in the products array.
[Route("Counties")]
public object GetCounties() {
var data = new County() {
Name = "xxx",
};
data.Products.Add(new Product() { Name="Cool Software", Date = DateTime.Now});
db.Counties.Add(data);
db.SaveChanges();
var da = db.Counties.ToList();
return db.Counties;
}
The reason you have having this issue is because the foreign keys are not correctly configured. Take a look at your database and look at the foreign keys. For Entity Framework to understand the relationships properly, you must mark related entities as virtual. So do this:
public virtual List<Product> Products { get; set;}
And then in your Product class add the navigation property back to the parent County:
public virtual County County { get; set;}
I found this tutorial really good:
http://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx
Hope this helps.

need guidelines in creating/updating related entities (is it possible with automapper?)

I'm new in ASP.NET MVC and would love to improve here. I used ASP.NET MVC + EF Code first approach. But I'm a little confuse on how to create/update related entites. So here's my scenario. Say,
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
public class Stock
{
public int Id { get; set; }
public int ItemId { get; set; }
public int StorageId { get; set; }
public float Amount { get; set; }
public virtual Item Item { get; set; }
public virtual Storage Storage { get; set; }
}
public class Storage
{
public int Id { get; set; }
public Name { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
So an Item has a 1:many relationship with Stock. And Storage has 1:many relationship with Stock
In displaying them I used Automapper which worked perfectly. (Thanks to SO for helping me)
Now, what I'm trying to achieve is.. How to create/update entites? (Is it possible to used Automapper here?)
Say, in one POST it will add an Item, with Stock, and with selected Storage. A sample code would be great for reference.
Any help would be much appreciated. Thanks
AutoMapper is just a tool to map the properties of the View Model to/from your Domain Model.
The View Model is what you use in all of your Views, and your Domain Model is the underlying business model which shouldn't be exposed to the Views.
This is what AutoMapper simplifies, it maps properties of these two models so that we don't have to keep on converting one model to the other.
Now moving on to creating / updating related entities...
Say we want to add new Stock using the navigation property on the Item.
Item item = this.DbSource.Items.First(itemEntity => itemEntity.Id == 5);
if(item.Stocks == null) item.Stocks = new Collection<Stock>();
item.Stocks.Add(new Stock
{
StorageId = 3,
Amount = 123F
});
this.DbSource.SaveChanges();
Another case that you just pointed out was having a new Item and X amount of Stock of that Item, which you want to store in the database in a single operation.
Storage storage = this.DbSource.Storages.First(storageEntity => storageEntity.Id == 3);
if(storage.Stocks == null) storage.Stocks = new Collection<Stock>();
Stock stock = new Stock
{
StorageId = 3,
Amount = 123F,
Item = new Item
{
Name = "Redbull"
}
};
storage.Stocks.Add(stock);
this.DbSource.SaveChanges();
Or if you have no data in your database and you want all 3 models posted in a single go...
Stock stock = new Stock
{
Amount = 123F,
Item = new Item
{
Name = "Redbull"
}
};
Storage storage = new Storage
{
Name = "It's a secret"
};
storage.Stocks.Add(stock);
this.DbSource.Storages.Add(storage);
this.DbSource.SaveChanges();
Also modify all of your Models with a constructor which initializes a Collection on all of your ICollection navigational properties, this way you can avoid the NullReferenceException
So for example modify the Item class to this
public class Item
{
public Item()
{
this.Stocks = new Collection<Stock>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
Using the Item as root property
Collection<Stock> stocks = new Collection<Stock>();
Collection<Stock> stocks.Add(new Stock
{
StorageId = 123,
Amount = 1000F
});
Item item = new Item
{
Name = "Pizza",
Stocks = stocks
};
this.DbSource.SaveChanges();

How to query a flatten sub collection in RavenDb? Index needed?

I am using RavenDb in C# web project. I have an object that I need to query its child collection with 1 row per child object and some of the root/parent object properties.
Note: This is not the actual design, just simplified for this question.
public class OrderLine
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public DateTime? ShipDate { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
public DateTime OrderDate { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
The order with the orderlines is one single document. ShipDate will be updated on each line because not all products are always in stock.
I need to be able to create a list of the last 10 products sent with the following columns:
OrderId
Customer
ProductName
ShipDate
This doesn't work because SelectMany is not supported:
var query = from helper in RavenSession.Query<Order>()
.SelectMany(l => l.OrderLines, (order, orderline) =>
new { order, orderline })
select new
{
helper.order.OrderId,
helper.order.CustomerName,
helper.orderline.ProductName,
helper.orderline.ShipDate
};
var result = query.Where(x => x.ShipDate.HasValue)
.OrderByDescending(x => x.ShipDate.Value).Take(10);
I believe the right thing to do isto create an Index that will flatten out the list but I haven't had any success. I don't believe a Map-Reduce situation will work because as I understand it will effectively does a group by which Reduces the number of documents to less rows (in the index). But in this case, I am trying to expand the number of documents to more rows (in the index).
I would rather not put each OrderLine in a separate document but I do not know what my options are.
Since you want to filter and sort by fields in the subclass, you'll need to make sure all the fields you want are indexed and stored.
public class ShippedItemsIndex
: AbstractIndexCreationTask<Order, ShippedItemsIndex.Result>
{
public class Result
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public DateTime ShipDate { get; set; }
}
public ShippedItemsIndex()
{
Map = orders =>
from order in orders
from line in order.OrderLines
where line.ShipDate != null
select new
{
order.OrderId,
order.CustomerName,
line.ProductName,
line.Quantity,
line.ShipDate
};
StoreAllFields(FieldStorage.Yes);
}
}
Then you can project from the index into your results.
var query = session.Query<Order, ShippedItemsIndex>()
.ProjectFromIndexFieldsInto<ShippedItemsIndex.Result>()
.OrderByDescending(x => x.ShipDate)
.Take(10);
var results = query.ToList();
Here is a complete test demonstrating.

Linq to Entities, Master Detail into DataContracts Query

This is part of my WebAPI, and I'm having trouble getting this data out of Linq to entities into my Datacontract objects. I will be returning a custom data type to the caller, whether they want it XML or JSON, I don't care, I'm just handing this to WebAPI to take care of it.
I'm using VS2013 and EF5, ASP.NET 4.5 in C#
The structure is as follows:
ProductCategory
ID
CategoryName
(a million other things)
List<Products>
Products
ID
ProductName
(a million other things)
Category
I have set up a DataContract that looks like the following:
ProductCategoryDataContract
ProductCategoryId
ProductCategoryName
List<ProductDataContract> Products
ProductDataContract
ProductName
Basically, I want to get a Linq query to return ALL categories, and within it ALL products.
from prodcat in context.ProductCategories order by prodcat.ItemOrder
select new ProductCategoryDataContract
{
ProductCategoryId = prodcat.Id
Products = prodcat.Products // this obviously fails as ProductDataContract != Products
}
If I try
Products = new List<ProductDataContract> { //initializer stuff }
I don't have any of the intellisense things I would think I would have (ProductName, etc), because I'm guessing I'm in the list.
Basically, I have all the relationships set up and I can get everything in straight up EF, but because I'm putting these into new datacontracts, it's giving me a little grief (mostly due to my lack of linq knowledge).
My question is:
1.) how can I do this
and
2.) how can I do this with minimal database hits. Potentially I'm firing off thousands of items within tens of product groups.
Thanks much, and if I'm not clear on anything, please lmk. And, the above is pseudocodish, not the real deal so if I made stupid naming errors, that's unlikely 'it' :)
public interface IProducts
{
int ProductId { get; set; }
decimal Price { get; set; }
List<IProductCategories> Categorieses { get; set; }
}
public interface IProductCategories
{
int ProductId { get; set; }
string ProductCategoryName { get; set; }
}
internal class Products : IProducts
{
public int ProductId { get; set; }
public decimal Price { get; set; }
public List<IProductCategories> Categorieses { get; set; }
}
internal class ProductCategories : IProductCategories
{
public int ProductId { get; set; }
public string ProductCategoryName { get; set; }
public ProductCategories(int productId, string productCategoryName)
{
ProductId = productId;
ProductCategoryName = productCategoryName;
}
}
public class ProductDataContract
{
public int ProductId { get; set; }
public List<IProductCategories> Categorieses { get; set; }
}
//Here is how you get your data:
// your retun objects
var products = new List<ProductDataContract>();
using (
var db =
new DataClassesDataContext(
ConfigurationManager.ConnectionStrings["TestConnectionString"].ConnectionString))
{
foreach (var prod in db.Products.Select(p => new Products {ProductId = p.ProductId}))
{
prod.Categorieses = new List<IProductCategories>();
foreach (var category in db.ProductCategories.Where(c => c.ProductId == prod.ProductId))
{
prod.Categorieses.Add(new ProductCategories(category.ProductId, category.ProductCategoryName));
}
products.Add(new ProductDataContract {Categorieses = prod.Categorieses, ProductId = prod.ProductId});
}
}

Calculated column in EF Code First

I need to have one column in my database calculated by database as (sum of rows) - (sum of rowsb). I'm using code-first model to create my database.
Here is what I mean:
public class Income {
[Key]
public int UserID { get; set; }
public double inSum { get; set; }
}
public class Outcome {
[Key]
public int UserID { get; set; }
public double outSum { get; set; }
}
public class FirstTable {
[Key]
public int UserID { get; set; }
public double Sum { get; set; }
// This needs to be calculated by DB as
// ( Select sum(inSum) FROM Income WHERE UserID = this.UserID)
// - (Select sum(outSum) FROM Outcome WHERE UserID = this.UserID)
}
How can I achieve this in EF CodeFirst?
You can create computed columns in your database tables. In the EF model you just annotate the corresponding properties with the DatabaseGenerated attribute:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public double Summ { get; private set; }
Or with fluent mapping:
modelBuilder.Entity<Income>().Property(t => t.Summ)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
As suggested by Matija Grcic and in a comment, it's a good idea to make the property private set, because you'd probably never want to set it in application code. Entity Framework has no problems with private setters.
Note: For EF .NET Core you should to use ValueGeneratedOnAddOrUpdate because HasDatabaseGeneratedOption doesnt exists, e.g.:
modelBuilder.Entity<Income>().Property(t => t.Summ)
.ValueGeneratedOnAddOrUpdate()
As of 2019, EF core allows you to have computed columns in a clean way with the fluent API:
Suppose that DisplayName is the computed column you want to define, you have to define the property as usual, possibly with a private property accessor to prevent assigning it
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// this will be computed
public string DisplayName { get; private set; }
}
Then, in the model builder, address it with the column definition:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Property(p => p.DisplayName)
// here is the computed query definition
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
}
For further information, have a look at MSDN.
public string ChargePointText { get; set; }
public class FirstTable
{
[Key]
public int UserID { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string Summ
{
get { return /* do your sum here */ }
private set { /* needed for EF */ }
}
}
References:
Bug in EF 4.1 DatabaseGeneratedOption.Computed
Calculated Columns in Entity Framework Code First Migrations
Working with Computed Columns
In EF6, you can just configure the mapping setting to ignore a calculated property, like this:
Define the calculation on the get property of your model:
public class Person
{
// ...
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
Then set it to ignore on the model configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//...
modelBuilder.Entity<Person>().Ignore(x => x.FullName)
}
One way is doing it with LINQ:
var userID = 1; // your ID
var income = dataContext.Income.First(i => i.UserID == userID);
var outcome = dataContext.Outcome.First(o => o.UserID == userID);
var summ = income.inSumm - outcome.outSumm;
You may do it within your POCO object public class FirstTable, but I would not suggest to, because I think it's not good design.
Another way would be using a SQL view. You can read a view like a table with Entity Framework. And within the view code, you may do calculations or whatever you want. Just create a view like
-- not tested
SELECT FirstTable.UserID, Income.inCome - Outcome.outCome
FROM FirstTable INNER JOIN Income
ON FirstTable.UserID = Income.UserID
INNER JOIN Outcome
ON FirstTable.UserID = Outcome.UserID
I would go about this by just using a view model. For example rather than have the FirstTable class as a db entity would you not be better just having a view model class called FirstTable and then have a function that is used to return this class that would include the calculated sum? For example your class would just be:
public class FirstTable {
public int UserID { get; set; }
public double Sum { get; set; }
}
And then you would have a function that you call that returns the calculated sum:
public FirsTable GetNetSumByUserID(int UserId)
{
double income = dbcontext.Income.Where(g => g.UserID == UserId).Select(f => f.inSum);
double expenses = dbcontext.Outcome.Where(g => g.UserID == UserId).Select(f => f.outSum);
double sum = (income - expense);
FirstTable _FirsTable = new FirstTable{ UserID = UserId, Sum = sum};
return _FirstTable;
}
Basically the same as an SQL view and as #Linus mentioned I don't think it would be a good idea keeping the computed value in the database. Just some thoughts.
I stumbled across this question when trying to have an EF Code First model with a string column "Slug", be derived from another string column "Name". The approach I took was slightly different but worked out well so I will share it here.
private string _name;
public string Name
{
get { return _name; }
set
{
_slug = value.ToUrlSlug(); // the magic happens here
_name = value; // but don't forget to set your name too!
}
}
public string Slug { get; private set; }
What is nice about this approach is you get the automatic slug generation, while never exposing the slug setter. The .ToUrlSlug() method isn't the important part of this post, you could use anything in its place to do the work you need done. Cheers!

Categories

Resources