EF6 async included entities properties are null? - c#

Here is the query:
using (var _context = GetCommonDataContext())
{
return await _context.AnswerSelectListEntries
.Include(asle => asle.Optional)
.OrderBy(asle => asle.DisplayOrder)
.Select(asle => new AnswerSelectListEntryDTO()
{
Id = asle.Id,
AnswerSelectListId = asle.AnswerSelectListId,
ShortAnswer = asle.ShortAnswer,
LongAnswer = asle.LongAnswer,
CounterpartyGroupId = asle.CounterpartyGroupId,
Optional = new AnswerDefDTO()
{
Id = asle.Optional.Id,
Name = asle.Optional.Name,
AnswerType = asle.Optional.AnswerType,
MultiplierAnswerType = asle.Optional.MultiplierAnswerType,
IntRangeLow = asle.Optional.IntRangeLow,
IntRangeHigh = asle.Optional.IntRangeHigh,
AnswerSelectListId = asle.Optional.AnswerSelectListId
},
AnswerDefId = asle.AnswerDefId,
PartyType = asle.PartyType,
DisplayOrder = asle.DisplayOrder
}).ToListAsync();
}
I am getting an error (thrown from the subquery) that Id, and AnswerType (which is an enum) cannot be cast to their counterparts (int and enum) because they are null. The issue is that they are non-nullable (and not null in the db). Somehow, asle.Optional's properties are being set to null (even though they are non-nullable). I am guessing this has something to do with the async but not sure what.
Fixed using:
Optional = asle.Optional == null ? null : new AnswerDefDTO()...

My guess would be that, since that thing is optional, and is making some kind of join, most likely a left join in the database, that the columns are coming back as DBNull simply because they weren't actually in the database. I would advise you to run a SELECT * FROM Optional where ID = whatever asle.Id is.

Related

Must declare the scalar variable - Dapper

I do multiple mapping on the dapper. Then I try to implement dapper builder
But the it return exception:
Must declare the scalar variable \"#ExecutionId\".\r\nInvalid usage of the option NEXT in the FETCH statement.
Without multiple mapping, never give a problem
Here my snippet code
var Builder = new SqlBuilder();
var SelectedQuery = Builder.AddTemplate(# "SELECT e.[Id], e.[BuyOrderBookId], e.[SellOrderBookId], e.[Volume], e.[Price], e.[CreationDate], e.[StatusId], bo.[UserId], bo.[MarketId], so.[UserId] FROM[dbo].[Execution] AS e JOIN[dbo].[OrderBook] AS bo ON e.BuyOrderBookId = bo.Id JOIN[dbo].[OrderBook] as so ON e.SellOrderBookId = so.Id
/**where**/
ORDER BY e.[CreationDate] DESC OFFSET #skip ROWS FETCH NEXT #take ROWS ONLY;
");
//Execution ID
if (filter.ExecutionId.HasValue)
Builder.Where("e.[Id] = #ExecutionId", new {ExecutionId = filter.ExecutionId.Value
});
var query = await connection.QueryAsync < ExecutionViewModel, OrderBookViewModel, OrderBookViewModel, ExecutionViewModel > (SelectedQuery.RawSql, (execute, buyOrder, sellOrder) => {
execute.BuyUserId = buyOrder.UserId;
execute.SellUserId = sellOrder.UserId;
execute.MarketId = buyOrder.MarketId;
return execute;
},
splitOn: "UserId,UserId",
param: new {
SelectedQuery.Parameters,
skip = (pagingParam.PageNumber - 1) * pagingParam.PageSize,
take = pagingParam.PageSize
});
Anyone know did I do something wrong here?
Update
I just fix like this
if (filter.ExecutionId.HasValue)
Builder.Where(String.Format("e.[Id] = {0}",filter.ExecutionId));
I believe this is not good way to implement. Is risk sql injection.
Try changing the where clause like this:
if (filter.ExecutionId.HasValue)
Builder.Where("e.[Id]", new {Id = filter.ExecutionId.Value});
You can add parameters like this.
if (filter.ExecutionId.HasValue)
{
Builder.Where("e.[Id] = #ExecutionId");
((DynamicParameters)SelectedQuery.Parameters)
.AddDynamicParams(new {
ExecutionId = filter.ExecutionId.Value
});
}

How can I edit or add to a particular field without pull the all object

How I can do just this ( a.myFavorits.Add()) without pulling the all object to var a , because a has a lot of data, and I don't want to pull all a object, but I can't find a way do do it.
I want to do the lambada and the linq without return something but linq is always return something
public static void addFavorits(long f,long idUser)
{
using (var db = dataBase())
{
// here i pull object user from users table
var a = db.users.Where(c => c.id == idUser).SingleOrDefault();
// here i adding to the object field myFavorits new value
//myFavorits is also a table of entitys that connected to user object
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.SaveChanges();
}
}
I thought to do something like this but i dont know how to set the field users_TableId that is the key that connect the 2 tables
public static void addFavorits(long favoritId,long idUser)
{
using (var db = dataBase())
{
db.favoritsUsersLong.Add(new BE.FavoritsUsersLong {myLong = favoritId}
/*,users_TableId =idUser*/);
db.SaveChanges();
}
}
Here's a concrete example that does what you want. In this example, only the Name of a Company is modified and saved. Or an item is added to one of its collections.
var cmp = new Company{ CmpId = 1, Name = "Cmp1" }; // CmpId is the primary key
db.Companies.Attach(cmp);
db.Entry(cmp).Property(c => c.Name).IsModified = true;
// Or add an entity to a collection:
cmp.Users = new[] {new User { Name = "a1", PassWord = "a1" } };
try
{
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
finally
{
db.Configuration.ValidateOnSaveEnabled = true;
}
Result in SQL:
DECLARE #0 VarChar(30) = 'Cmp1'
DECLARE #1 Int = 1
UPDATE [dbo].[Company]
SET [Name] = #0
WHERE ([CmpId] = #1)
There are a few things to note here:
Obviously you need to know the Id of the entity you want to modify.
The object you create is called a stub entity, which is an incomplete entity. When you try to save such an entity, EF is very likely to complain about null values in required properties. That's why almost certain you'd have to disable validation (temporarily, or, better, dispose the context immediately).
If you want to add an item to a collection, you should leave validation enabled, because you'd want to know for sure that the new entity is valid. So you shouldn't mix these two ways to use a stub entity.
If you often need roughly the same small part of your entity you may consider table splitting.
I'm guessing this is what you want? I don't see you 'editting' I only see you adding.
using (var db = dataBase())
{
var a = new user();
....
//set properties etc..
...
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.users.Add(a);
db.SaveChanges();
}

adding NULL to DropdownList-options

On my ASP.NET MVC4 application, I need a dropdown-list to select a Location and insert its ID into a table, say News.
Some news apply to ALL Locations - therefore the LocationID in News is Nullable, to indicate this.
So I need to add "ALL Locations" to the dropdown.
This is how I imagined, it would work - but on the last line Value = DBNull.Value (or simple null) is not allowed. It only accepts an integer. I CANNOT use "0" as foreign key constraints on that table do not allow 0, because there is no ID=0 in Locations-table
var locations = db.Locations.Select(c => new { DisplayText = c.Location, Value = c.ID }).ToList();
locations.Insert(0, new { DisplayText = "All Locations", Value = DBNull.Value });
return Json(new { Result = "OK", Options = locations});
How can I add this to the options-list?
The basis of your locations anonymous object is created in the new {} operator in your Linq statement. If you need to allow for NULL, then... new { DisplayText = c.Location, Value = (int?)c.ID } explicitly set the anonymous type's Value parameter as nullable int. Then you can do Value = null on the next line when inserting.

How to work around NotMapped properties in queries?

I have method that looks like this:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = db.Organizations.Select(org => new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
SiteCount = org.Sites.Count(),
DbSecureFileCount = 0,
DbFileCount = 0
});
return results;
}
This is returns results pretty promptly.
However, you'll notice the OrganizationViewModel has to properties which are getting set with "0". There are properties in the Organization model which I added via a partial class and decorated with [NotMapped]: UnsecureFileCount and SecureFileCount.
If I change those 0s to something useful...
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount
... I get the "Only initializers, entity members, and entity navigation properties are supported" exception. I find this a little confusing because I don't feel I'm asking the database about them, I'm only setting properties of the view model.
However, since EF isn't listening to my argument I tried a different approach:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = new List<OrganizationViewModel>();
foreach (var org in db.Organizations)
{
results.Add(new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
});
}
return results;
}
Technically this gives me the correct results without an exception but it takes forever. (By "forever" I mean more than 60 seconds whereas the first version delivers results in under a second.)
Is there a way to optimize the second approach? Or is there a way to get the first approach to work?
Another option would be to load the values back as an anonymous type and the loop through those to load your viewmodel (n+1 is most likely the reason for the slowness).
For example:
var results = db.Organizations.Select(org => new
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
}).ToList();
var viewmodels = results.Select( x=> new OrganizationViewModel
{
Id = x.Id,
Name = x.Name,
DbSecureFileCount = x.DbSecureFileCount,
DbFileCount = x.DbFileCount,
SiteCount = x.SiteCount
});
Sorry about the formatting; I'm typing on a phone.
You are basically lazy loading each object at each iteration of the loop, causing n+1 queries.
What you should do is bring in the entire collection into memory, and use it from there.
Sample code:
var organizationList = db.Organizations.Load();
foreach (var org in organizationList.Local)
{
//Here you are free to do whatever you want
}

Entity Framework 4 - Duplicate Key Upon Update

I'm having trouble performing an update in the Entity Framework. I don't really understand the behaviour I am seeing.
I am using the AdventureWorks database.
The starting value for the StateProvince is Micronesia. If I change it to Maryland, the update is successful. However, if I then try to change it back to Micronesia, I get the following error:
"Cannot insert duplicate key row in object 'Sales.SalesTerritory' with
unique index 'AK_SalesTerritory_Name'.\r\nThe statement has been
terminated."
The DAL method in question is:
public static void UpdateCustomer(CustomerDetails customerDetails)
{
AWEntities context = Common.GetContext();
var customerQuery = from c in context.Individuals
.Include("Contact")
.Include("Customer.CustomerAddresses.Address.StateProvince.SalesTerritory")
//.Include("Customer.SalesTerritory.StateProvinces")
.Where(id => id.CustomerID == customerDetails.CustomerId)
select c;
var individual = customerQuery.ToList().ElementAt(0);
Contact contact = individual.Contact;
contact.LastName = customerDetails.LastName;
contact.MiddleName = customerDetails.MiddleName;
contact.FirstName = customerDetails.FirstName;
contact.EmailAddress = customerDetails.EmailAddress;
contact.Phone = customerDetails.Phone;
contact.Title = customerDetails.Title;
AWModel.Customer customer = individual.Customer;
customer.CustomerID = customerDetails.CustomerId;
customer.SalesTerritory.Name = customerDetails.SalesTerritory;
Address address = individual.Customer.CustomerAddresses.ElementAt(0).Address;
address.AddressLine1 = customerDetails.AddressLine1;
address.AddressLine2 = customerDetails.AddressLine2;
address.City = customerDetails.City;
address.PostalCode = customerDetails.PostalCode;
address.StateProvinceID = customerDetails.SalesProvinceId;
context.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
}
Can anyone identify the correct way to do what I am attempting.
This is occurring when you update the SalesTerritory.Name property:
customer.SalesTerritory.Name = customerDetails.SalesTerritory;
The effect is to change the SalesTerritory entity, rather than the customer entity. I believe you want something more like:
customer.SalesTerritoryID = customerDetails.SalesTerritoryID;

Categories

Resources