I have a code that gets all products from my DB:
using (var entities = new DataEntities())
{
var products = entities.Products.AsQueryable();
if (!string.IsNullOrEmpty(nameFilter))
{
products = products.Where(o => o.Name.Contains(nameFilter));
}
var result = products.Select(ProductBuilder.CreateProductDto);
return result.ToList();
}
CreateProductDto method:
public static ProductDto CreateProductDto(this Product product)
{
return new ProductDto
{
Id = product.Id,
Name = product.Name,
IsEnabled = product.IsEnabled,
KeyPairDto = new KeyPairDto()
{
Id = product.KeyPair.Id,
EncryptedPrivateExponent = product.KeyPair.EncryptedPrivateExponent,
Modulus = product.KeyPair.Modulus,
PublicExponent = product.KeyPair.PublicExponent,
},
};
}
It works fine on my colleaugue's machine. But I get EntityCommandExecutionException with the folloing inner exception: There is already an open DataReader associated with this Command which must be closed first. when trying to access product.KeyPair.
Interesting thing is that if I refresh product.KeyPair via Debugger - it loads fine.
Add
MultipleActiveResultSets=true
to the provider part of the connection string within your entity framework connection string (i.e. the part the defines the data source, initial catalog, etc)
Related
I currently have a project that I'm working on, which has a database connected to it. In said database I need to query some tables that don't have a relationship. I need to get a specific set of data in order to display it on my user interface. However I need to be able to reference the returned data put it into a list and convert it into json. I have a stored procedure that needs to just be executed against the context because it's retrieving data from many different tables.
I've tried using ExecuteSqlCommand but that doesn't work, because it returns -1 and can't put it into a list.
I've tried using linq to select the columns I want however it's really messy and I cannot retrieve the data as easily.
I've tried using FromSql, however that needs a model to execute against the context which is exactly what I don't want.
public string GetUserSessions(Guid memberId)
{
string sql = $"EXECUTE dbo.GetUserTrackByMemberID #p0";
var session = _context.Database.ExecuteSqlCommand(sql, memberId);
var json = JsonConvert.SerializeObject(session);
return json;
}
This is the ExecuteSqlCommand example, this returns -1 and cannot be put into a list as there will be more than one session.
public string GetUserSessions(Guid memberId)
{
var session = _context.MemberSession.Where(ms => ms.MemberId == memberId).Select(s => new Session() { SessionId =
s.SessionId, EventId = s.Session.EventId, CarCategory = s.Session.CarCategory, AirTemp = s.Session.AirTemp,
TrackTemp = s.Session.TrackTemp, Weather = s.Session.Weather, NumberOfLaps = s.Session.NumberOfLaps, SessionLength = s.Session.SessionLength,
Event = new Event() { EventId = s.Session.Event.EventId, TrackId = s.Session.Event.TrackId, Name = s.Session.Event.Name, NumberOfSessions =
s.Session.Event.NumberOfSessions, DateStart = s.Session.Event.DateStart, DateFinish = s.Session.Event.DateFinish, TyreSet = s.Session.Event.TyreSet,
Track = new Track() { TrackId = s.Session.Event.Track.TrackId, Name = s.Session.Event.Track.Name, Location = s.Session.Event.Track.Location, TrackLength
= s.Session.Event.Track.TrackLength, NumberOfCorners = s.Session.Event.Track.NumberOfCorners} } });
var json = JsonConvert.SerializeObject(session);
return json;
}
This is using Linq, however it's really messy and I feel there's probably a better way to do this, and then when retrieving the data from json it's a lot bigger pain.
public string GetUserSessions(Guid memberId)
{
var session = _context.MemberSession.FromSql($"EXECUTE dbo.GetUserSessionByMemberID {memberId}").ToList();
var json = JsonConvert.SerializeObject(session);
return json;
}
This is the ideal way I would like to do it, however since I'm using the MemberSession model it will only retrieve that data from the stored procedure which is in the MemberSession table, however I want data that is in other tables as well....
public string GetUserSessions(Guid memberId)
{
var session = _context.MemberSession.Where(ms => ms.MemberId == memberId).Include("Session").Include("Event").ToList();
var json = JsonConvert.SerializeObject(session);
return json;
}
I tried this way but because the Event table has no reference / relationship to MemberSession it returns an error.
As I've previously stated in the RawSql example I'm only getting the table data that is in the MemberSession table, no other tables.
There are no error messages.
using (var context = new DBEntities())
{
string query = $"Exec [dbo].[YOUR_SP]";
List<ResponseList> obj = context.Database.SqlQuery<ResponseList>(query).ToList();
string JSONString = JsonConvert.SerializeObject(obj);
}
Consider the following database structure (created using EF Code First)
Using simple code as illustrated below I can easily add either new Customers or Suppliers;
private static void InsertCustomers()
{
var customer1 = new Customer
{
FirstName = "Mickey",
LastName = "Mouse",
DateStarted = DateTime.Now
};
var customer2 = new Customer
{
FirstName = "Fred",
LastName = "Flintstone",
DateStarted = DateTime.Now
};
using (var context = new ContactsContext())
{
context.Database.Log = Console.Write; //purely for logging
context.Customers.Add(customer1);
context.Customers.Add(customer2);
context.SaveChanges();
}
}
My question is simple. Fred Flintstone could at some point in the future become a supplier as well as remaining a Customer. From within SSMS I can easily achieve this but I would obviously like to do this from within the application itself. If I use the syntax
var sup = new Supplier();
then the underlying logic will create a new Id (which is perfectly sensible, but undesired as I want to use the existing Id assigned to Fred Flinstone as a Customer which is 2) so how do I in effect add an existing Contacts.Id into the Suppliers table so that it becomes a Primary / foreign key using code in my application.
Well it would transpire that this is not as intuitive in Code First as one might have thought so I have reverted to using the following which does work as intended;
static void CreateSupplier(int id)
{
var contactId = id; //the id being passed in would have been collected from a list of Customers, as we want Fred Flintstone it would have been set to 2
var date = DateTime.UtcNow;
using (var context = new ContactsContext())
{
context.Database.Log = Console.Write; //purely for logging
context.Database.ExecuteSqlCommand(
"INSERT INTO Contacts.Suppliers(Id, DateStarted) VALUES({0}, {1})", contactId, date);
context.SaveChanges();
}
}
It's possible that there is a way to do this with a lambda but at present I haven't found one.
I am trying to access a stored procedure that retrieves a page using an id.
But I am getting an error:
Error 1 Cannot implicitly convert type
'System.Data.Entity.Core.Objects.ObjectResult<StorePageCMS.Models.mn_StorePage_Select_One_Result>' to 'StorePageCMS.Models.StorePage'
I am not sure how to fix this. The stored precedure that comes from dbEntities from SQL Server, does take an int parameter.
Any help is much appreciated.
public StorePage Get(int StorePageID)
{
using (dbEntities db = new dbEntities())
{
StorePage storepage = db.mn_StorePage_Select_One(StorePageID);
if (storepage == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return storepage;
}
}
UPDATE
I rewrote the method this way:
public List<StorePage> Get(int StorePageID)
{
List<StorePage> storepagelist = new List<StorePage>();
using (dbEntities db = new dbEntities())
{
var results = db.mn_StorePage_Select_One(StorePageID).ToList();
foreach (var result in results)
{
var storepage = new StorePage()
{
StorePageID = result.StorePageID,
SPPreambleID = result.SPPreambleID,
Title = result.Title,
SEOTitle = result.SEOTitle,
ParentStorePageID = result.ParentStorePageID ?? -1,
Meta = result.Meta,
Image = result.Image,
ImageLink = result.ImageLink,
Blurb = result.Blurb,
RegionID = result.RegionID,
Footer = result.Footer
};
storepagelist.Add(storepage);
}
return storepagelist;
}
}
Does this looks more correct?
2 UPDATE
Does this looks correct?
If you're not using the Code First model of Entity Framework
Since StorePageCMS.Models.mn_StorePage_Select_One_Result has no conversion to StorePage, I'm assuming it's a stored procedure result. If that's a stored procedure result (of mn_StorePage_Select_One), you need to map it's result to the StorePage model instead in the EDMX designer.
Here, you'd need to say it returns a collection of StorePageCMS.Models.StorePage Entities.
Can anyone tell where I make a mistake ? :( I want to insert a row using this. It's just not working. I also tried to use "context.SaveChanges();" but nothing changed. No insert at all, and no exception.
public List<string> Add_Address(string address, int selected_item)
{
List<string> list = new List<string>();
using(var context = new RSS_Reader_Database())
{
Address Address = new Address();
Category_Address Category_Address = new Category_Address();
Address.URL = address.ToString();
int max_id = Convert.ToInt32(context.Addresses.OrderByDescending(t => t.ID_Address).FirstOrDefault());
Category_Address.ID_Address = max_id;
Category_Address.ID_Category = selected_item+1;
var select_query = from t in context.Addresses select t.URL;
foreach (var element in select_query)
{
list.Add(element);
}
}
return list;
}
Edit: Following all Your advices, I made something that works. Looking at this code above, I have no idea what I was trying to do yesterday. Thanks a lot.
public List<string> Add_Address(string address, int selected_item)
{
List<string> list = new List<string>();
using(var context = new RSS_Reader_Database())
{
Address Address = new Address() { URL = address };
context.Addresses.Add(Address);
context.SaveChanges();
int max_id = context.Addresses.Max(u => u.ID_Address);
Category_Address Category_Address = new Category_Address() { ID_Address = max_id, ID_Category = selected_item + 1 };
context.Categories_Addresses.Add(Category_Address);
context.SaveChanges();
var query = from t in context.Addresses
select t.URL;
var data = query.ToList();
foreach (var element in data)
{
list.Add(element);
}
}
return list;
}
Saving with Entity Framework generally works like this. Using your above code as a starting point.
using(var context = new RSS_Reader_Database())
{
Address address = new Address();
// Set address properties
context.Addresses.Add(address);
context.SaveChanges();
}
You need to add the object to the DbSet<T> where T is the type of the entity that is defined on the DbContext. You then need to call SaveChanges() on the context.
I would suggest reading this. It is an easy to follow introduction to Entity Framework.
Not sure exactly what you are trying to do.
But if you are expecting to insert the data by the list.Add(element); command it won't work.
If you are planning to insert data into the same DB, you need to use one property from the context to represent the List collection add a new element on this property.
Something like:
context.Lists.Add(element);
if you want retrieve data, you should not call SaveChanges() !,
try get all values from one query like this:
List<string> select_query = (from t in context.Addresses select t.URL).ToList();
I am trying to populate a POCO with the result of a query to my DB. The resultset has multiple rows so I'm just iterating over each result and populating the POCO with the data and I get this error :"There is already an open DataReader associated with this Connection which must be closed first."
Where am I messing up?
it goes like this:
[Controller.cs]
/// <summary>
/// Populates the inner Ads list
/// </summary>
public void FetchAds()
{
_ads = new List<Ad>();
using (var context = (ApartmentDataEntities) DbContextFactory.GetInstance().GetDbContext<ApartmentDataEntities>())
{
foreach (var config in context.Configurations)
{
_ads.Add(new AdModel(_basePath, config));
}
}
AdsReady.SafeTrigger(this, new AdArray { Ads = _ads.ToArray() });
}
[AdModel.cs] (inherits from the POCO)
public AdModel(String baseFolder, Configuration apartment)
{
_baseFolder = baseFolder;
GetAd(apartment);
}
/// <summary>
/// Populates the Ad from the Database
/// </summary>
private void GetAd(Configuration apartment)
{
PropertyId = apartment.Property.id;
PropertyName = apartment.Property.name;
PropertyPhone = apartment.Property.phone;
PropertyAddress = apartment.Property.address;
AreaName = apartment.Property.MapArea.areaName;
RegionName = apartment.Property.MapArea.Region.name;
PropertyZipCode = apartment.Property.zipCode;
ComissionRate = apartment.Property.comissionRate;
Images = apartment.Property.Images.Select(img => img.id).ToArray();
YearBuilt = apartment.Property.yearBuilt;
Features = apartment.Property.features;
Ammenities = apartment.Property.ammenities;
CommunitySpecial = apartment.Property.communitySpecial;
PetPolicy = apartment.Property.petPolicy;
Size = apartment.size;
Bathrooms = apartment.bathrooms;
Bedrooms = apartment.bedrooms;
Price = apartment.price;
PropertyImages = apartment.Property.Images.Select(img => img.imageContents).ToArray();
FloorplanImage = null;
Floorplan = null;
var configFloorplan = apartment.Images.SingleOrDefault();
if (configFloorplan == null) return;
FloorplanImage = configFloorplan.imageContents;
Floorplan = configFloorplan.id;
}
Usually it is a better idea to do the projection to another model in the query itself. Like
_ads = (from apartment in context.Configurations
let configFloorplan = apartment.Images.SingleOrDefault()
select new AdModel
{
PropertyId = apartment.Property.id,
PropertyName = apartment.Property.name,
PropertyPhone = apartment.Property.phone,
...
PropertyImages = apartment.Property.Images
.Select(img => img.imageContents),
FloorplanImage = configFloorplan.imageContents,
Floorplan = configFloorplan.id
}).ToList();
This ensures that everything is executed as one query. The problem with your approach is that while EF is reading context.Configurations other queries get executed to populate additional properties of the model.
This could be solved (maybe) by enabling multiple active result sets (MARS) in the connection string. But that does not fix the fact that you will be executing a potentially large number of queries (two queries for each materialized model).
Are you using EntityFramework? It's probably that the .Select's in your GetAdd are lazy loading and thus trying to query the DB while the outer query is still open. Try adding toArray() after context.Configurations. Seems I had to sprinkle these all over the place to avoid that error.