How to convert linq entitySet AND CHILDREN to lists? - asp.net

I ran into an error when trying to serialize a linq entitySet. To get around this i converted the entitySet to a list. The problem I have run into now is that it's child entity sets are not converting to a list and when I try to serialize the parent those are now throwing an error. Does anyone know of a way to convert a linq entitySet AND it's children to lists?
p.s. I'm new to linq so if any of this dosn't make sense let me know

Just project onto new types:
var q = from e in Entities
select new
{
Id = e.Id,
Name = e.Name,
Children = from c in e.Children
select new
{
Id = c.Id,
Name = c.Name,
// etc.
},
// etc.
};
var s = serializer.Serialize(q);

I am guessing you are trying to serialize to XML.
Either way, the problem stems from the circular references in LINQ entity objects. Lets say you have a main table Customers with a second table Orders. The Customers entity has a reference to all of the Orders this customer has (typically Customer.Orders). The Orders entity has a reference to the customer entity (typically Order.Customer). When the serializer tries to serialize this object, it recognizes the circular reference and throws an error.
In order to do what you want, you have to write your own serializer or convert the objects to something that can be directly serialized, like custom serialization objects which contain just the information you want to show up in the serialized version.

Related

Linq to entity enum not comparing

Here is my Enum:
public enum AdvertStatus
{
Active,
Archived
}
And my entity type:
public record Advertisement
{
...
public AdvertStatus Status { get; set; }
...
}
In database it's stored as int, Database is Postgree
When I try to compare it like so:
data = data.Where(x => x.Status == searchValues.Status);
Entity Framework throws an exception sayng:
.Status == (int)__searchValues_Status_3)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
I tried solutions from this question: LINQ TO ENTITY cannot compare to enumeration types but it did't work.
EDIT 1:
data is database table context IQueryable<AdvertisementDTO>
searchValues.Status is type of AdvertStatus from search filter
The issue may be higher up in your Linq query, such as you are attempting to project with a Select or ProjectTo before filtering. For simple types like int/string this should work, but depending on how your DTO is declared you might be introducing problems for mpgsql.
For instance if your query is something like:
var query = _context.Advertisements
.Select(x => new AdvertisementDTO
{
// populate DTO
}).Where(x => x.Status == searchValues.Status)
// ....
then npgsql may be having issues attempting to resolve the types between what is in the DTO and the enumeration in your searchValues. From what the exception detail looks like, npgsql is trying to be "safe" with the enum and casting to intbut feeding that to PostgreSQL that results in invalid SQL. I did some quick checks and the DTO would need to be using the same Enum type (C# complains if the DTO cast the value to int, cannot use == between AdvertStatus and int fortunately) The project may have something like a value converter or other hook trying to translate enumerations which is getting brought into the mix and gunking up the works.
Try performing the Where conditions prior to projection:
var query = _context.Advertisements
.Where(x => x.Status == searchValues.Status)
.Select(x => new AdvertisementDTO
{
// populate DTO
})
// ....
If the data value is stored as an Int then this should work out of the box. npgsql does support mapping to string (which would require a ValueConverter) as well as database declared enumerations. (https://www.postgresql.org/docs/current/datatype-enum.html) However, Int columns should work fine /w enums.
If that doesn't work, I'd try with a new DbContext instance pointed at the DB and a simple entity with that Enum to load a row from that table to eliminate whether npgsql is translating the enum correctly, just to eliminate any possible converters or other code that the main DbContext/models/DTOs may be contributing.
It was all my mistake in higher repo Select projection.
Thanks you all for help. Cheers.

LINQ and web service cannot return anonymous types, and you cannot construct an object in a query?

Web services cannot return an anonymous type.
If you are building a LINQ query using classes through a datacontext... you cannot construct instances of those classes in a query.
Why would I want to do this? Say I want to join three "tables" or sets of objects. I have three items with a foreign key to each other. And say the lowest, most detailed of these was represented by a class that had fields from the other two to represent the data from those. In my LINQ query I would want to return a list of the lowest, most detailed class. This is one way I have decided to "join some tables together" and return data from each of them via LINQ to SQL via a WebService. This may be bad practice. I certainly do not like adding the additional properties to the lowest level class.
Consider something like this... (please ignore the naming conventions, they are driven by internal consideration) also for some reason I need to instantiate an anonymous type for the join... I don't know why that is... if I do not do it this way I get an error...
from su in _dataContext.GetTable<StateUpdate>()
join sfs in _dataContext.GetTable<SystemFacetState>()
on new { su.lngSystemFacetState } equals new { lngSystemFacetState = sfs.lngSystemFacetState }
join sf in _dataContext.GetTable<SystemFacet>()
on new { sfs.lngSystemFacet } equals new { lngSystemFacet = sf.lngSystemFacet }
join s in _dataContext.GetTable<System>()
on new { sf.lngSystem } equals new {lngSystem = s.lngSystem}
select new
{
lngStateUpdate = su.lngStateUpdate,
strSystemFacet = sf.strSystemFacet,
strSystemFacetState = sfs.strSystemFacetState,
dtmStateUpdate = su.dtmStateUpdate,
dtmEndTime = su.dtmEndTime,
lngDuration = su.lngDuration,
strSystem = s.strSystem
}
).ToList();
Notice I have to build the anonymous type which is composed of pieces of each type. Then I have to do something like this... (convert it to a known type for transport via the web service)
result = new List<StateUpdate>(from a in qr select(new StateUpdate
{
lngStateUpdate = a.lngStateUpdate,
strSystemFacet = a.strSystemFacet,
strSystemFacetState = a.strSystemFacetState,
dtmStateUpdate = a.dtmStateUpdate,
dtmEndTime = a.dtmEndTime,
lngDuration = a.lngDuration,
strSystem = a.strSystem
}));
It is just awful. And perhaps I have created an awful mess. If I am way way off track here please guide me to the light. I feel I am missing something fundamental here when I am adding all these "unmapped" properties to the StateUpdate class.
I hope someone can see what I am doing here so I can get a better way to do it.
You can create a 'dto' class which just contains the properties you need to return and populate it instead of the anonymous object:
public class Result
{
public string lngStateUpdate
{
get;
set;
}
... // other properties
}
then use it like this:
from su in _dataContext.GetTable<StateUpdate>()
...
select new Result
{
lngStateUpdate = su.lngStateUpdate,
... // other properties
}
Nitpick note - please ditch the Hungarian notation and camel casing for properties :)
I think the answer is to create another object to serve as a DTO. This object would not be mapped to the data context and can contain fields that cross the mapped objects. This solves the problems of repetitive properties in the mapped objects, and allows for instantiation of the DTO class in the query as it is not mapped.
FYI: with respect to the problem with the join- I revisited that and I think I may have had the inner and outer components of the join switched around before.

Magic Casting to collection of results

In the Simple.Data examples, there is an example of 'Magic Casting':
// When you assign the dynamic record to a static type, any matching properties are auto-mapped.
var db = Database.Open();
Customer customer = db.Customers.FindByCustomerId(1);
Does Simple.Data also magically cast if there are multiple records returned? Something like this:
var db = Database.Open();
IEnumerable<Customer> customers = db.Customers.FindBySurname("Smith");
Obviously I have tried the above and it doesn't work ("Cannot implicitly convert type" from SimpleQuery to my concrete type). Any advice would be welcome.
FindBySurname returns a single record. If you use FindAllBySurname you'll get an enumerable, which should magic cast OK. (If for some reason it doesn't, you can call .Cast() on it.)

attaching an entity with a related entity to a new entity framework context

Im trying to get my head around attaching an entity with a related entity to a new context when I want to update the entity.
I have a Person Table (Generalised to Personnel), which has a LanguageID field. This field is linked as a FK via the EF to another table Language with LanguageID as the primary key (1-M). I need to update a particular Persons language preference, however, the relationship seems to remain linked to the old context as i get a "Object cannot be referenced by multiple instances of IEntityChangeTracker" error on the line marked below. Is there any way to attach the Language entity to the new context as a relationship of the Personnel (Person) entity???
The entities were not detached in the orginal GetPersonnel() method which uses an .Include() method to return the PreferredLanguage
PreferredLanguage is the NavigationProperty name on the Person table...
public static void UpdateUser(Personnel originalUser, Personnel newUser )
{
using (AdminModel TheModel = new AdminModel())
{
((IEntityWithChangeTracker)originalUser).SetChangeTracker(null);
((IEntityWithChangeTracker)originalUser.PreferredLanguage).SetChangeTracker(null);
TheModel.Attach(originalUser);--Error Line
TheModel.ApplyPropertyChanges("Person", newUser);
TheModel.SaveChanges();
}
}
Thanks
Sean
To avoid these sort of problems you should make GetPersonnel() do a NoTracking query.
I.e.
ctx.Person.MergeOption = MergeOption.NoTracking;
// and then query as per normal.
This way you can get a graph of connected entities (assuming you use .Include()) that is NOT attached. Note this won't work if you try to manually detach entities, because doing so schreds your graph.
Hope this helps
Alex

MVC - Insert data into multiple entities from same page

I have a single page which collects related data into a series of input fields.
When the user clicks submit I want to insert the data into two different database tables in one go. ie I want to get the primary key from the first insert, and then use it for the second insert operation.
I am hoping to do this all in one go, but I am not sure of the best way to do this with the Models/Entities in MVC.
Are you using LINQ or some other ORM? Typically these will support the ability to add a related entity and handle the foreign key relationships automatically. Typically, what I would do with LINQtoSQL is something like:
var modelA = new ModelA();
var modelB = new ModelB();
UpdateModel( modelA, new string[] { "aProp1", "aProp2", ... } );
UpdateModel( modelB, new string[] { "bProp1", "bProp2", ... } );
using (var context = new DBContext())
{
modelA.ModelB = modelB;
context.ModelA.InsertOnSubmit( modelA );
context.SubmitChanges();
}
This will automatically handle the insertion of modelA and modelB and make sure that the primary key for modelA is set properly in the foreign key for modelB.
If you are doing it by hand, you may have to do it in three steps inside a transaction, first inserting modelA, getting the key from that insert and updating modelB, then inserting modelB with a's data.
EDIT: I've shown this using UpdateModel but you could also do it with model binding with the parameters to the method. This can be a little tricky with multiple models, requiring you have have separate prefixes and use the bind attribute to specify which form parameters go with which model.

Resources