When I do a lunq query on an EF model, does it not get child entities we well? I have a Transaction table, which has link to a Payee, and a transaction type entity. Also, each transaction has a list of transaction lines...
But the code bellow - all the child objects seen to be NULL, yet the data in the actual entity (Date) seems OK. But in the line: t.account.account_id; .... 'account' is NULL.
public static List<AccountTransactionDto> GetTransaction()
{
var trans = (from t in Db.account_transaction
select t).ToList();
List<AccountTransactionDto> al = new List<AccountTransactionDto>();
foreach(var t in trans)
{
AccountTransactionDto a = new AccountTransactionDto();
a.AccountId = t.account.account_id;
a.AccountTransactionId = t.account_transaction_id;
a.PayeeId = t.payee.payee_id;
a.TransactionDate = t.transaction_date;
a.TransactionTypeId = t.z_transaction_type.transaction_type_id;
foreach(var tl in t.account_transaction_line)
{
AccountTransactionLineDto l = new AccountTransactionLineDto();
l.AccountTransactionLineId = tl.account_transaction_line_id;
l.Amount = tl.amount;
l.BudgetId = tl.budget.budget_id;
l.CostCenterId = tl.cost_centre.cost_centre_id;
l.SubCategoryId = tl.sub_category.sub_category_id;
a.AccountTransactionLine.Add(l);
}
al.Add(a);
}
return al;
}
You have two options. You can enable the Lazy Loading via:
Db.ContextOptions.LazyLoadingEnabled = true;
Or if you change the query line to this (exact syntax may not be correct for Include):
var trans = (from t in Db.account_transaction
select t).Include("account_transaction.account_transaction_line");
Then it should pull back the child records with the parent record in a single result set. But this has performance penalties if there is a great amount of data.
Lazy loading needs to be enabled on your data context.
Db.ContextOptions.LazyLoadingEnabled = true;
or you need to explicitly tell EF to load the association. i.e.
var trans = (from t in Db.account_transaction.Include('account').Include('payee') select t).ToList();
Related
Is it possible to update objects with Entity Framework, without grabbing them first?
Example: Here, I have a function that provides a Primary Key to locate the objects, pulls them, then updates them. I would like to eliminate having to pull the objects first, and simply run an UPDATE query. Removing the need for the SELECT query being generated.
public async Task<int> UpdateChecks(long? acctId, string payorname, string checkaccountnumber, string checkroutingnumber, string checkaccounttype)
{
using (var max = new Max(_max.ConnectionString))
{
var payments = await
max.payments.Where(
w =>
w.maindatabaseid == acctId && (w.paymentstatus == "PENDING" || w.paymentstatus == "HOLD")).ToListAsync();
payments.AsParallel().ForAll(payment =>
{
payment.payorname = payorname;
payment.checkaccountnumber = checkaccountnumber;
payment.checkroutingnumber = checkroutingnumber;
payment.checkaccounttype = checkaccounttype;
payment.paymentmethod = "CHECK";
payment.paymentstatus = "HOLD";
});
await max.SaveChangesAsync();
return payments.Count;
}
}
You can use the Attach() command to attach an entity you already know exists and then call SaveChanges() will will call the appropriate update method. Here is some sample code from the MSDN article on the topic:
on the subject:
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Entry(existingBlog).State = EntityState.Unchanged;
// Do some more work...
context.SaveChanges();
}
Note that this is general EF logic, not related to any specific database implementation.
var FileProducts = from ProductsRow in ProductRangesDt.AsEnumerable()
join Filee in FileTb.AsEnumerable() on ProductsRow["GEN_CODE"].ToString() equals Filee["GEN_CODE"].ToString()
select new
{
PRODUCT_ID = ProductsRow["PRODUCT_ID"],
PRODUCT_NAME = ProductsRow["PRODUCT_NAME"],
PROVIDER_ID = ProductsRow["PROVIDER_ID"],
PROVIDER_NAME = ProductsRow["PROVIDER_NAME"],
GEN_CODE = ProductsRow["GEN_CODE"],
MIN_QUANTITY = Filee["MIN_QUANTITY"],
MAX_QUANTITY = Filee["MAX_QUANTITY"],
DISCOUNT_VALUE = Filee["DISCOUNT_VALUE"]
};
var s = (from b in FileProducts
select b.PRODUCT_ID).Distinct(); // count=285
var Products = (from ProductsRow in ProductRangesDt.AsEnumerable()
select ProductsRow["PRODUCT_ID"]).Distinct(); // count=7159
var result = Products.Except(s); // it's count should be 7159-285
I want to get all the products ID that are in Products and don't exist in FileProducts how can i do this ? result always return 0 as count
From the MSDN documentation about Except extension method:
This method is implemented by using deferred execution. The immediate
return value is an object that stores all the information that is
required to perform the action. The query represented by this method
is not executed until the object is enumerated either by calling its
GetEnumerator method directly or by using foreach in Visual C# or For
Each in Visual Basic.
So in order to get the real value form your Set differentiation, you need to enumerate your result either by a call to the Count()-Method (result.Count()) on using foreach (foreach (var r in result) { ... }).
I can't test with your data, but with test data at my disposition, the Except-extension did delivered the correct results.
I am writing many (20+) parent child datasets to the database, and EF is requiring me to savechanges between each set, without which it complains about not being able to figure out the primary key. Can the data be flushed to the SQL Server so that EF can get the primary keys back from the identities, with the SaveChanges being sent at the end of writing all of the changes?
foreach (var itemCount in itemCounts)
{
var addItemTracking = new ItemTracking
{
availabilityStatusID = availabilityStatusId,
itemBatchId = itemCount.ItemBatchId,
locationID = locationId,
serialNumber = serialNumber,
trackingQuantityOnHand = itemCount.CycleQuantity
};
_context.ItemTrackings.Add(addItemTracking);
_context.SaveChanges();
var addInventoryTransaction = new InventoryTransaction
{
activityHistoryID = newInventoryTransaction.activityHistoryID,
itemTrackingID = addItemTracking.ItemTrackingID,
personID = newInventoryTransaction.personID,
usageTransactionTypeId = newInventoryTransaction.usageTransactionTypeId,
transactionDate = newInventoryTransaction.transactionDate,
usageQuantity = usageMultiplier * itemCount.CycleQuantity
};
_context.InventoryTransactions.Add(addInventoryTransaction);
_context.SaveChanges();
}
I would like to do my SaveChanges just once at the end of the big loop.
You don`t need to save changes every time if you use objects refernces to newly created objects not IDs:
var addItemTracking = new ItemTracking
{
...
}
_context.ItemTrackings.Add(addItemTracking);
var addInventoryTransaction = new InventoryTransaction
{
itemTracking = addItemTracking,
...
};
_context.InventoryTransactions.Add(addInventoryTransaction);
...
_context.SaveChanges();
Since they're all new items rather than
itemTrackingID = addItemTracking.ItemTrackingID,
you could go with
addItemTracking.InventoryTransaction = addInventoryTransaction;
(or whatever the associated navigation property is) and pull the _context.SaveChanges() out of the loop entirely. Entity Framework is very good at inserting object graphs when everything is new. When saving object graphs containing both new and existing items setting the associated id is always safer.
How about:
var trackingItems = itemCounts
.Select(i => new ItemTracking
{
availabilityStatusID = availabilityStatusId,
itemBatchId = i.ItemBatchId,
locationID = locationId,
serialNumber = serialNumber,
trackingQuantityOnHand = i.CycleQuantity
});
_context.ItemTrackings.AddRange(trackingItems);
_context.SaveChanges();
var inventoryTransactions = trackingItems
.Select(t => new InventoryTransaction
{
activityHistoryID = newInventoryTransaction.activityHistoryID,
itemTrackingID = t.ItemTrackingID,
personID = newInventoryTransaction.personID,
usageTransactionTypeId = newInventoryTransaction.usageTransactionTypeId,
transactionDate = newInventoryTransaction.transactionDate,
usageQuantity = usageMultiplier * t.trackingQuantityOnHand
});
_context.InventoryTransactions.AddRange(inventoryTransactions);
_context.SaveChanges();
However I haven't worked with EF for quite a while and above code is written in notepad so I cannot vouch for it
Entity properties can be split across different tables, meaning that a single Entity can have it's columns mapped to different tables. How does one then, in code, retrieve the info on the particular table an entity property is mapped to.
foreach(PropertyInfo pi in typeof(DbContext).GetProperties())
{
if(pi.PropertyType.IsGenericType && pi.PropertyType.Name.Contains("DbSet"))
{
var t = pi.PropertyType.GetGenericArguments().FirstOrDefault();
var tables = t.GetCustomAttributes(true).OfType<TableAttribute>();
foreach (var entityProperty in t.GetProperties())
{
if (entityProperty.GetCustomAttributes(true).OfType<RequiredAttribute>().Any<RequiredAttribute>())
{
var fieldname = entity.Name;
//I need to match this column with the table it belongs to here
}
}
}
}
So far, I have the code below to get at the entity property, from the object itself, how do I determine the particular table the current property is mapped to, in my database? Thanks in advance.
Consider using the OR/M for the actual generation of the SQL you actually need.
var dbContext = new DbContext();
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var set = objectContext.Set<Foo>();
var query = from x in set
where {stuff}
select new { x.Bar, x.Baz ...};
var objectQuery = (ObjectQuery)query;
var command = objectQuery.CommandText;
var parameters = objectQuery.Parameters;
So basically I have this field in XML that I want to update. It is parsed out through XML and I am not sure how to update this single record from this single instance.
var dataContext =
new RequestFormsDBDataContext(ConfigManager.Data.ConnectionString);
var userForm = (
from i in dataContext.RequestFormItems
join c in dataContext.RequestFormInstances on i.TypeGuid equals c.TypeGuid
where i.Id == FormID
select new {
i.Id,
XmlData = XElement.Parse(i.XML)
}
).ToList();
// Parsing out XML Data
var userFormParced = (
from i in userForm
select new FormItem {
FormId = i.Id,
DateTimeCompleted = i.XmlData.Element("DateTimeCompleted").Value
}
).FirstOrDefault();
RFDateTimeCompleted = userFormParced.DateTimeCompleted;
// Code that isnt working
userFormParced.DateTimeCompleted = "New Data";
dataContext.SubmitChanges();
This won't work because you aren't changing the instance that you retrieve from the database. You are creating a new object using the values from the original - twice - and then changing it. The LINQ-to-SQL context has no knowledge that you're changing database rows, just some unrelated XML that you've constructed.
Firstly, the value you retrieve from the database is being put into an XElement
XmlData = XElement.Parse(i.XML)
and then you retrieve the 'value' from a node and put it into another new object
select new FormItem
{
FormId = i.Id,
DateTimeCompleted = i.XmlData.Element("DateTimeCompleted").Value
}
It's lost any reference to the LINQ-to-SQL context by this stage. Instead you need to change it in place.
If you're using XML in your database, you should be using XML columns which map to XML properties in your LINQ-to-SQL objects. You're not, so we'll implement a workaround.
Try something like this.
// just get the item
var userForm = (
from i in dataContext.RequestFormItems
join c in dataContext.RequestFormInstances on i.TypeGuid equals c.TypeGuid
where i.Id == FormID
select i).FirstOrDefault();
// parse the XML
var xml = XElement.Parse(userForm.XML);
RFDateTimeCompleted = xml.Element("DateTimeCompleted").Value;
xml.Element("DateTimeCompleted").Value = "New Data";
// and finally, because you're again just changing XML
// unrelated to the context, update the original object
userForm.XML = xml.ToString();
context.SubmitChanges();