This is the code which I'm not convinced. Please check how I'm passing as parameter the entity Collection.
public ExamProduced GetExamProduced(XElement xml)
{
var examProduced = new ExamProduced
{
ExamProducedID = (int)xml.Attribute("ExamID"),
Date = (DateTime)xml.Attribute("Date"),
Seed = (int)xml.Attribute("Seed"),
//Exercises = GetExercises(xml)
};
GetExercises(xml, examProduced.Exercises);
return examProduced;
}
public void GetExercises(XElement xml, EntityCollection<Exercise> entityCollection)
{
var objs =
from objective in xml.Descendants("Objective")
where (bool)objective.Attribute("Produced")
let id = (int)objective.Attribute("ID")
let id2 = (Objective)entityService.Objectives.Where(o => o.ObjectiveID == id).FirstOrDefault()
select new Exercise
{
Objective = id2,
MakeUp = ...
Quantify = ...
Score = ...
};
foreach (var exercise in objs)
{
entityCollection.Add(exercise);
}
}
If not, I'll receiving an error. Like this with this code.
public ExamProduced GetExamProduced(XElement xml)
{
var examProduced = new ExamProduced
{
ExamProducedID = (int)xml.Attribute("ExamID"),
Date = (DateTime)xml.Attribute("Date"),
Seed = (int)xml.Attribute("Seed"),
Exercises = GetExercises(xml)
};
return examProduced;
}
public EntityCollection<Exercise> GetExercises(XElement xml)
{
var objs =
from objective in xml.Descendants("Objective")
where (bool)objective.Attribute("Produced")
let id = (int)objective.Attribute("ID")
select new Exercise
{
ExerciseID = id,
MakeUp = (bool)objective.Attribute("MakeUp"),
Quantify = (byte)(int)objective.Attribute("Quantify"),
Score = (float)objective.Elements().Last().Attribute("Result")
};
var entityCollection = new EntityCollection<Exercise>();
foreach (var exercise in objs)
entityCollection.Add(exercise);
return entityCollection;
}
The error I am getting is below:
InvalidOperationException was unhandled.
The object could not be added to the EntityCollection or
EntityReference. An object that is attached to an ObjectContext cannot
be added to an EntityCollection or EntityReference that is not
associated with a source object.
I hope that from the comments I'm understanding you correctly... If not, I'll update this answer.
Firstly, your EntityCollection<Exercise> GetExercises(XElement xml) is not going to work. As the error message says, you cannot construct a random EntityCollection like that: the EntityCollection needs the object to be attached to the context because it automatically synchronises its list with the context. And since you aren't saying what to attach it to anywhere, it won't work. The only way to make it work would be to avoid creating an EntityCollection in the first place.
Your void GetExercises(XElement xml, EntityCollection<Exercise> entityCollection) procedure could work. However, you need to make sure to actually have an EntityCollection<Exercise> instance to be able to call it. The way you're creating your ExamProduced object, it isn't attached to the context by the time you return from GetExamProduced, so it isn't possible to have a EntityCollection<Exercise> property for it at that point.
Perhaps the easiest way to get things working would be to pass your context to the GetExamProduced method, and let them get attached to the context automatically. I'm assuming it's a common ObjectContext, but you can update it as needed:
public ExamProduced GetExamProduced(XElement xml, YourContext context)
{
var examProduced = new ExamProduced()
{
ExamProducedID = (int)xml.Attribute("ExamID"),
Date = (DateTime)xml.Attribute("Date"),
Seed = (int)xml.Attribute("Seed")
};
context.ExamsProduced.Attach(examProduced);
LoadExercises(xml, context, examProduced);
// examProduced.Exercises should be available at this point
return examProduced;
}
public void LoadExercises(XElement xml, YourContext context, ExamProduced examProduced)
{
foreach (var exercise in
from objective in xml.Descendants("Objective")
where (bool)objective.Attribute("Produced")
let id = (int)objective.Attribute("ID")
let id2 = (Objective)entityService.Objectives.Where(o => o.ObjectiveID == id).FirstOrDefault()
select new Exercise
{
ExamProduced = examProduced,
Objective = id2,
MakeUp = ...
Quantify = ...
Score = ...
}))
{
context.Exercises.Attach(exercise);
}
}
I don't know if these are new objects that should be added in the database, or if these objects are expected to exist in the database already. I've assumed the latter. If the former, .Attach should be updated to .AddObject in two places.
Is this what you're looking for, or did I misunderstand?
Related
I am creating stored procedures in SQL Server database and have been having issues returning the data when called.
I have managed to get it to work, but it feels as if it is a hack job and that I am doing it incorrectly. Please see the code below and let me know if there is a better way to go about doing this. Thank you for taking the time to help me.
create procedure FetchSumOfEmpSalariesByCity
as
begin
select
sum(e.SAL) as TotalSalary, d.LOC as Location
from
EMPS e
join
DEPTs d on e.DEPTNO = d.DEPTNO
group by
d.LOC
end
public class SumOfEmpsSalaryByCity
{
[Key]
public int TotalSalary { get; set; }
public string Location { get; set; }
}
[HttpGet]
[Route("salary")]
public IHttpActionResult GetEMPsSal()
{
using (var db = new KemmitContext())
{
var sp = db.SumOfEmpsSalaryByCities.SqlQuery("FetchSumOfEmpSalariesByCity");
return Ok(sp.ToList());
}
}
I want to do this the correct way. Is there a way to do this without a model? Or am I going about this the right way?
I break these tasks down like this; should it be done with EF or in the database and if it's in the database, should it be a View or an Sp?
Whenever I'm simply selecting data, I use EF either direct to the table for very simple queries or I create a database View for any joins, etc. This can be done in EF but it's god-awful, in any case, IMO these tasks belong in the database, right tool, right job. If you're using code-first, getting your Views across is a bit involved, let me know if you're doing that.
var model = db.v_ObservationAutoComplete // This can be direct to a table or a view
.Where(oa => oa.Observation.Contains(term))
.OrderBy(oa => oa.Observation)
.Select(oa => new
{
label = oa.Observation
}).Take(10);
When I have to update something in a single table, I use EF
t_Section eSection = new t_Section
{
SectionId = model.SectionId,
Section = model.Section,
SectionTypeId = model.SectionTypeId,
SectionOrdinal = model.SectionOrdinal,
ModifyDate = DateTime.Now,
ModifyUserName = User.Identity.Name,
LastChangeId = newChangeId
};
db.Entry(eSection).State = EntityState.Modified;
db.SaveChanges();
If I have to do a multi-table update, I have a couple of different methodologies; 1) returning a simple bool value for a status code/scalar value, or I have to return a result set after doing whatever update I made.
This returns a List
List<string> elements = new List<string>();
try
{
SqlParameter[] parms = new[]
{
new SqlParameter("mpid", myProtocolsId),
new SqlParameter("elid", elementId)
};
elements = db.Database.SqlQuery<string>("p_MyProtocolsOverviewElementRemove #myProtocolsId = #mpid, #elementId = #elid", parms).ToList();
return Json(elements);
}
catch (Exception e)
{
return Json(null);
}
And if I just need a simple value back, something like this;
SqlParameter[] parms = new[]
{
new SqlParameter("chid", changeId),
new SqlParameter("prid", change.ObjectId),
new SqlParameter("psid", change.ProtocolSectionId),
new SqlParameter("inid", change.SecondaryObjectId),
new SqlParameter("acun", User.Identity.Name)
};
result = db.Database.SqlQuery<int>("p_MyProtocolsContentUpdateInterventionAdd #changeId = #chid, #protocolId = #prid, #protocolSectionId = #psid, #interventionId = #inid, #acceptUserName = #acun", parms).FirstOrDefault();
Hope this helps!
I have this method:
public DemographicData GetDemographicByZipCode(string zipcode)
{
DemographicData demoData = new DemographicData();
using(var context = new DataContext())
{
var result = from item in context.Demographic
where item.ZipCode == zipcode
select item;
foreach (var data in result)
{
demoData.City = data.City;
demoData.State = data.State;
demoData.Zip = data.ZipCode;
}
}
return demoData;
}
I am attempting to write the method without the loop as indicated below but as apparent, it will not work because I cannot use an assignment operator within the expression tree.
public DemographicData GetDemographicByZipCode(string zipcode)
{
DemographicData demoData = null;
// Instantiate to new instance in the select method.
// I need to use this instance demoData
using(var context = new DataContext())
{
var result = from item in context.Demographic
where item.ZipCode == zipcode
select new DemographicData()
{
//assign data to instance member here.
};
}
return demoData;
}
No, you can't do that. But if your goal is for demoData to represent a single result from your query, then you can do something like this:
public DemographicData GetDemographicByZipCode(string zipcode)
{
DemographicData demoData = null;
using(var context = new DataContext())
{
demoData = (from item in context.Demographic
where item.ZipCode == zipcode
select new DemographicData()
{
Zip = item.ZipCode,
City = item.City,
State = item.State
}).FirstOrDefault();
}
//Do other stuff to demoData here, if needed
return demoData;
}
That uses FirstOrDefault to get the first one in the list (or null if there are none). In the loop in your example, you're just overwriting the values, so I assume you are only expecting one result.
Update: If you are expecting more than one result, then return IEnumerable<DemographicData>, like this:
public IEnumerable<DemographicData> GetDemographicByZipCode(string zipcode)
{
List<DemographicData> demoData = null;
using(var context = new DataContext())
{
demoData = (from item in context.Demographic
where item.ZipCode == zipcode
select new DemographicData()
{
Zip = item.ZipCode,
City = item.City,
State = item.State
}).ToList();
}
//Do other stuff to demoData here, if needed
return demoData;
}
Use List<DemographicData> and ToList() inside the method to force it to actually perform the query there. If you don't use ToList(), it will perform the query when the list is first accessed, which will be outside of the using, when your context is disposed. It might also complain about multiple enumerations, depending on your code.
try:
var result = from item in context.Demographic
where item.ZipCode == zipcode
select item;
This will work.
City = item.City,
State = item.State,
Zip = item.ZipCode
What is still wrong with your code is that you are returning single DemographicData object whereas result will be collection of DemographicData objects even if there is only one that fulfills the condition tem.ZipCode == zipcode.
If you are expecting only exactly one instance of DemographicData do it this way
var result = (from item in context.Demographic
.
(your query here)
.
).Single();
If you are expecting one or zero then replace Single() with FirstOrDefault(). If you are expecting a collection then the return type of that method should be IEnumerable<DemographicData> or IQueryable<DemographicData> or any other that suits you best. If you need list/ array then replace Single() with ToList()/ToArray().
I have been using AutoMapper which I find really good.
But in current project I can't use AutoMapper because few reasons. lets not go into that.
Wondering if below is best way of manual mapping? Seems like it will be lot of looping depending on complexity of the model and one to many relationship.
Is there best or efficient way of doing manual mapping.
Basket basket = StoreRepository.GetBasket(BasketCode); // where BasketCode is xyz
List<ProductResponse> basketProducts = new List<ProductResponse>();
foreach (Product product in basket.Products) {
basketProducts.Add(new ProductResponse()
{
Name = product.Name,
Description = product.Description
});
}
BasketResponse result = new BasketResponse()
{
BasketCode = basket.Code,
Description = basket.Description,
IntroMessage = basket.IntroMessage,
Products = basketProducts
};
return result;
Above is snippet from a method of Web API Controller which return Json.
I am using manual mapping to reduce detailed complexity and create required Json structure and data.
You would probably be better off creating some kind of general function for each class so you don't have to repeat the mapping everytime you want to use it. For example:
public static class Mapper
{
public static ProductResponse Map(Product product)
{
return new ProductResponse
{
Name = product.Name,
Description = product.Description
};
}
public static BasketResponse Map(Basket basket)
{
return new BasketResponse
{
BasketCode = basket.Code,
Description = basket.Description,
IntroMessage = basket.IntroMessage,
Products = basket.Products.Select(a => Mapper.Map(a))
};
}
}
May be this question should be in code review. Anyway you can simplify your code by using Select LINQ query instead of FOREACH loop to build basketProducts list, like below.
List<ProductResponse> basketProducts = basket.Products.Select(product=> new ProductResponse()
{
Name = product.Name,
Description = product.Description
}).ToList();
You can simplify this further by writing a new delegate for mapping which can be reused at multiple places.
Func<Product, ProductResponse> MapProduct = delegate (Product product)
{
return new ProductResponse()
{
Name = product.Name,
Description = product.Description
};
};
then your code will be much simpler like below. Use the new delegate in SELECT method of LINQ query.
Basket basket = StoreRepository.GetBasket(BasketCode);
BasketResponse result = new BasketResponse()
{
BasketCode = basket.Code,
Description = basket.Description,
IntroMessage = basket.IntroMessage,
Products = basket.Products.Select(MapProduct).ToList()
};
return result;
I am stuck with a stack overflow exception in this section of code, it is obviously occurring because the Customer object calls a list of CustomerBackgroundLevel objects, each one of which creates a new customer object. I am trying to find a way around the issue, any help would be appreciated..
Customer Constructor -
public CustomerVO(Customer item)
{
CustomerID = item.CustomerID;
CustomerName = item.CustomerName;
ECNNumber = item.ECNNumber;
CustomerBackgroundLevels = item.CustomerBackgroundLevels.Select(c => new CustomerBackgroundLevelVO(c)).ToList();
}
Customer Background Level Constructor -
public CustomerBackgroundLevelVO(CustomerBackgroundLevel item)
{
CustomerBackgroundLevelID = item.CustomerBackgroundLevelID;
CustomerID = item.CustomerID;
BackgroundLevelID = item.BackgroundLevelID;
StartDate = item.StartDate;
EndDate = item.EndDate;
Customer = new CustomerVO(item.Customer);
BackgroundLevel = new BackgroundLevelVO(item.BackgroundLevel);
}
Customer Get Method -
public CustomerVO GetByID(int id)
{
var item = repository.AsQueryable().Where(x => x.CustomerID == id).FirstOrDefault();
if (item == null)
return null;
return new CustomerVO(item);
}
Yeah, as you've stated creating new objects in a loop like that is gonna lead to nothing good.
Instead of creating all these wrapper objects in your constructors, why don't you wrap them on demand? That is, when you execute some code that needs a CustomerVO object, create the CustomerVO object within that function and then let it go out of scope when the function ends.
You can solve your loop like this:
public CustomerVO(Customer item)
{
CustomerID = item.CustomerID;
CustomerName = item.CustomerName;
ECNNumber = item.ECNNumber;
**CustomerBackgroundLevels = item.CustomerBackgroundLevels.Select(c => new CustomerBackgroundLevelVO(c,this)).ToList();
}
**public CustomerBackgroundLevelVO(CustomerBackgroundLevel item, CustomerVO vocustomer)
{
CustomerBackgroundLevelID = item.CustomerBackgroundLevelID;
CustomerID = item.CustomerID;
BackgroundLevelID = item.BackgroundLevelID;
StartDate = item.StartDate;
EndDate = item.EndDate;
**Customer = vocustomer;
BackgroundLevel = new BackgroundLevelVO(item.BackgroundLevel);
}
Is that a copy constructor? If so, you need to create a custom constructor for copying the item rather than using it in both scenarios where you new up an object and copy it.
return new CustomerVO(item);
The above is unnecessary and the problem line is:
Customer = new CustomerVO(item.Customer);
Change the above line to this:
Customer = item.Customer;
Unless you are having reference problems, which means you need to design a new constructor.
And if the item.Customer object is not a CustomerVO object, then you will need to pass the reference of the current CustomerVO object into the constructor of the CustomerBackgroundLevelVO.
I think I understand returning records of an anonymous type from But in this I want to create NEW CatalogEntries, and set them from the values selected. (context is a Devart LinqConnect database context, which lets me grab a view).
My solution works, but it seems clumsy. I want to do this in one from statement.
var query = from it in context.Viewbostons
select it;
foreach (GPLContext.Viewboston item in query)
{
CatalogEntry card = new CatalogEntry();
card.idx = item.Idx;
card.product = item.Product;
card.size = (long)item.SizeBytes;
card.date = item.Date.ToString();
card.type = item.Type;
card.classification = item.Classification;
card.distributor = item.Distributor;
card.egplDate = item.EgplDate.ToString();
card.classificationVal = (int)item.ClassificationInt;
card.handling = item.Handling;
card.creator = item.Creator;
card.datum = item.Datum;
card.elevation = (int)item.ElevationFt;
card.description = item.Description;
card.dirLocation = item.DoLocation;
card.bbox = item.Bbox;
card.uniqID = item.UniqId;
values.Add(card);
}
CatalogResults response = new CatalogResults();
I just tried this:
var query2 = from item in context.Viewbostons
select new CatalogResults
{ item.Idx,
item.Product,
(long)item.SizeBytes,
item.Date.ToString(),
item.Type,
item.Classification,
item.Distributor,
item.EgplDate.ToString(),
(int)item.ClassificationInt,
item.Handling,
item.Creator,
item.Datum,
(int)item.ElevationFt,
item.Description,
item.DoLocation,
item.Bbox,
item.UniqId
};
But I get the following error:
Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a
collection initializer because it does not implement
'System.Collections.IEnumerable' C:\Users\ysg4206\Documents\Visual
Studio
2010\Projects\CatalogService\CatalogService\CatalogService.svc.cs 91 25 CatalogService
I should tell you what the definition of the CatalogResults is that I want to return:
[DataContract]
public class CatalogResults
{
CatalogEntry[] _results;
[DataMember]
public CatalogEntry[] results
{
get { return _results; }
set { _results = value; }
}
}
My mind is dull today, apologies to all. You are being helpful. The end result is going to be serialized by WCF to a JSON structure, I need the array wrapped in a object with some information about size, etc.
Since .NET 3.0 you can use object initializer like shown below:
var catalogResults = new CatalogResults
{
results = context.Viewbostons
.Select(it => new CatalogEntry
{
idx = it.Idx,
product = it.Product,
...
})
.ToArray()
};
So if this is only one place where you are using CatalogEntry property setters - make all properties read-only so CatalogEntry will be immutable.
MSDN, Object initializer:
Object initializers let you assign values to any accessible fields or properties of an
object at creation time without having to explicitly invoke a constructor.
The trick here is to create a IQueryable, and then take the FirstOrDefault() value as your response (if you want a single response) or ToArray() (if you want an array). The error you are getting (Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a collection initializer because it does not implement 'System.Collections.IEnumerable') is because you're trying to create an IEnumerable within the CatalogEntry object (by referencing the item variable).
var response = (from item in context.Viewbostons
select new CatalogEntry()
{
idx = item.Idx,
product = item.Product,
size = (long)item.SizeBytes,
...
}).ToArray();
You don't have to create anonymous types in a Linq select. You can specify your real type.
var query = context.Viewbostons.Select( it =>
new CatalogEntry
{
idx = it.idx,
... etc
});
This should work:
var query = from it in context.Viewbostons
select new CatalogEntry()
{
// ...
};