I am trying to add a note to my event object. I am getting an error using this code
Note noteToAdd = new Note { State = State.Added, NoteText = note };
Patient patient = context.Patients.Find(patientId);
patient.State = State.Modified;
patient.MobilePatient.State = State.Modified;
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
context.ApplyStateChanges();
Is there a better way to do it using Linq To Entity?
The error that I am having is :
{"Invalid column name 'Note_Id'."}
and the SQl that is being generated is a SELECT instead of INSERT.
Thank you
but your map shows a one-to-many relation between Note and Event...
all of your code remain as they are, but instead of this line :
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
replace these lines:
noteToAdd.EventID = oEvent.ID; // replace field names, to exactly what they are;
context.Note.Add(noteToAdd);
var oEvent = patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid);
oEvent.NoteID = noteToAdd.ID; // replace field names, to exactly what they are;
also i think if you don`t write these two:
var oEvent = patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid);
oEvent.NoteID = noteToAdd.ID; // replace field names, to exactly what they are;
there is not any problem, i`m not sure
According to your map, Event entity has a list of Note as navigation property, and i think you should add to this collection instead, what you write in this line:
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
i think should be like this:
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Add(noteToAdd);
in addition, what kind of error you get ? can you explain your error ?
are sure there is no add method on Event navigation property
why don`t you try to add note from context directly? like:
context.Note.Add(noteToAdd);
Related
I want to have my EfCore query translated into the following SQL query:
select
c.blablabla
from
codes c
left join lookups l on c.codeId = l.entityid and l.languageCode = <variable - language code of current thread> and l.lookuptype = 'CODE'
where
..something..
order by
l.displayname
Note: tables 'codes' and 'lookups' are not connected! 'lookups' contains a lot of different lookup data in different languages!
I am stuck into limitations of EfCore (like 'NavigationExpandingExpressionVisitor' failed). I don't want to make in-memory filtering, it looks silly to me... Am I missing something obvious?
In perspective, I'd like to make universal method to help sort by displayname (or other lookup name) for different kind of entities - not only codes.
Seems like I figured it out. If there's a better approach - please let me know:
protected override IQueryable<FixCode> SortByDisplayName(IQueryable<FixCode> queryable, string languageCode = null)
{
return queryable
.GroupJoin(
DbContext.FixCodeValues.Where(x =>
x.DomainId == CentralToolConsts.Domains.CENTRAL_TOOLS
&& x.CodeName == CentralToolsFieldTypes.CODE_ORIGIN
&& (x.LanguageCode == languageCode || x.LanguageCode == CentralToolsDbLanguageCodes.English)),
//TODO: this will be a 'selector' parameter
code => code.CodeOriginId,
codeOrigin => codeOrigin.StringValue,
(c, co) => new
{
Code = c,
CodeOrigin = co
}
)
.SelectMany(
x => x.CodeOrigin.DefaultIfEmpty(),
(x, codeOrigin) => new { Code = x.Code, CodeOrigin = codeOrigin }
)
.OrderBy(x => x.CodeOrigin.ShortName)
.Select(x => x.Code);
}
var eventDetails = (from e in db.tb_Event
select new
{
e.EventID,
e.tb_Customer.CustomerName,
e.StartDate,
e.EndDate,
loc = (from l in db.tb_EventLocation where l.EventID == e.EventID select new { l.tb_Location.LocationName }).Distinct(),
e.Objective
});
This is the correct way do do it. Try this out, this is working fine for me. Since you cannot use Include() for filtering you have to use projection technique.
Spent a lot of time, but still cann't understand how to avoid caching in DbContext.
I attached below entity model of some easy case to demonstrate what I mean.
The problem is that dbcontext caching results. For example, I have next code for querying data from my database:
using (TestContext ctx = new TestContext())
{
var res = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == false
select p
}
}).AsEnumerable().Select(x => x.b).Single();
}
In this case, everything is fine: I got what I want (Only persons with Archived == false).
But if I add another query after it, for example, query for buildings that have people that have Archived flag set to true, I have next things, that I really cann't understand:
my previous result, that is res, will be added by data (there
will be added Persons with Archived == true too)
new result will contain absolutely all Person's, no matter what Archived equals
the code of this query is next:
using (TestContext ctx = new TestContext())
{
var res = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == false
select p
}
}).AsEnumerable().Select(x => x.b).Single();
var newResult = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == true
select p
}
}).AsEnumerable().Select(x => x.b).Single();
}
By the way, I set LazyLoadingEnabled to false in constructor of TestContext.
Does anybody know how to workaround this problem? How can I have in my query what I really write in my linq to entity?
P.S. #Ladislav may be you can help?
You can use the AsNoTracking method on your query.
var res = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == false
select p
}
}).AsNoTracking().AsEnumerabe().Select(x => x.b).Single();
I also want to note that your AsEnumerable is probably doing more harm than good. If you remove it, the Select(x => x.b) will be translated to SQL. As is, you are selecting everything, then throwing away everything but x.b in memory.
have you tried something like:
ctx.Persons.Where(x => x.Flat.Building.Id == 1 && x.Archived == false);
===== EDIT =====
In this case I think you approach is, imho, really hazardous. Indeed you works on the data loaded by EF to interpret your query rather than on data resulting of the interpretation of your query. If one day EF changes is loading policy (for example with a predictive pre-loading) your approach will "send you in then wall".
For your goal, you will have to eager load the data you need to build your "filterd" entity. That is select the building, then foreach Flat select the non archived persons.
Another solution is to use too separate contexts in an "UnitOfWork" like design.
When calling Max() on an IQueryable and there are zero records I get the following exception.
The cast to value type 'Int32' failed because the materialized value is null.
Either the result type's generic parameter or the query must use a nullable type.
var version = ctx.Entries
.Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
.Max(e => e.Version);
Now I understand why this happens my question is how is the best way to do this if the table can be empty. The code below works and solves this problem, but its very ugly is there no MaxOrDefault() concept?
int? version = ctx.Entries
.Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
.Select(e => (int?)e.Version)
.Max();
Yes, casting to Nullable of T is the recommended way to deal with the problem in LINQ to Entities queries. Having a MaxOrDefault() method that has the right signature sounds like an interesting idea, but you would simply need an additional version for each method that presents this issue, which wouldn't scale very well.
This is one of many mismatches between how things work in the CLR and how they actually work on a database server. The Max() method’s signature has been defined this way because the result type is expected to be exactly the same as the input type on the CLR. But on a database server the result can be null. For that reason, you need to cast the input (although depending on how you write your query it might be enough to cast the output) to a Nullable of T.
Here is a solution that looks slightly simpler than what you have above:
var version = ctx.Entries
.Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
.Max(e =>(int?)e.Version);
Try this to create a default for your max.
int version = ctx.Entries
.Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
.Max(e =>(int?)e.Version) ?? 0;
You could write a simple extension method like this, it returns the default value of type T if no records exist and is then apply Max to that or the query if records exist.
public static T MaxOrEmpty<T>(this IQueryable<T> query)
{
return query.DefaultIfEmpty().Max();
}
and you could use it like this
maxId = context.Competition.Select(x=>x.CompetitionId).MaxOrEmpty();
I couldnt take no for an answer :) I have tested the below and it works, I havent checked the SQL generated yet so be careful, I will update this once I have tested more.
var test = ctx.Entries
.Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
.MaxOrDefault(x => x.Version);
public static TResult? MaxOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
where TResult : struct
{
return source
.Select(selector)
.Cast<TResult?>()
.Max();
}
Try this:
IEnumerable<AlertsResultset> alerts = null;
alerts = (from POA in SDSEntities.Context.SDS_PRODUCT_ORDER_ALERT
join A in SDSEntities.Context.SDS_ALERT on POA.ALERT_ID equals A.ALERT_ID
orderby POA.DATE_ADDED descending
select new AlertsResultset
{
ID = POA.PRODUCT_ORDER_ALERT_ID == null ? 0:POA.PRODUCT_ORDER_ALERT_ID ,
ITEM_ID = POA.ORDER_ID.HasValue ? POA.ORDER_ID.Value : POA.PRODUCT_ID.Value,
Date = POA.DATE_ADDED.Value,
orderType = SDSEntities.Context.SDS_ORDER.Where(o => o.ORDER_ID == POA.ORDER_ID.Value).FirstOrDefault().ORDER_TYPE,
TransactionNumber = POA.PRODUCT_ID.HasValue ? (SDSEntities.Context.SDS_PRODUCT.Where(p => p.PRODUCT_ID == POA.PRODUCT_ID.Value).FirstOrDefault().TRANSACTION_NUMBER) : (SDSEntities.Context.SDS_ORDER.Where(o => o.ORDER_ID == POA.ORDER_ID.Value).FirstOrDefault().TRANSACTION_NUMBER),
Publisher = POA.PRODUCT_ID.HasValue ?
(
SDSEntities.Context.SDS_PRODUCT.Where(p => p.PRODUCT_ID == POA.PRODUCT_ID.Value).FirstOrDefault().PRODUCT_TYPE_NUMBER == "ISSUE" ? (from prod in SDSEntities.Context.SDS_PRODUCT
join ji in SDSEntities.Context.SDS_JOURNAL_ISSUE on prod.PRODUCT_ID equals ji.PRODUCT_ID
join j in SDSEntities.Context.SDS_JOURNAL on ji.JOURNAL_ID equals j.JOURNAL_ID
where prod.PRODUCT_ID == POA.PRODUCT_ID
select new { j.PUBLISHER_NAME }).FirstOrDefault().PUBLISHER_NAME : (from prod in SDSEntities.Context.SDS_PRODUCT
join bi in SDSEntities.Context.SDS_BOOK_INSTANCE on prod.PRODUCT_ID equals bi.PRODUCT_ID
join b in SDSEntities.Context.SDS_BOOK on bi.BOOK_ID equals b.BOOK_ID
where prod.PRODUCT_ID == POA.PRODUCT_ID
select new { b.PUBLISHER_NAME }).FirstOrDefault().PUBLISHER_NAME
)
: (SDSEntities.Context.SDS_ORDER.Where(o => o.ORDER_ID == POA.ORDER_ID.Value).FirstOrDefault().PUBLISHER_NAME),
Alert = A.ALERT_NAME,
AlertType = A.ALERT_TYPE,
IsFlagged = POA.IS_FLAGGED.Value,
Status = POA.ALERT_STATUS
});
how about
var version = ctx.Entries
.Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
.Max(e => (int?)e.Version);
less ugly, more elegant
I want to suggest a merge from the existing answers:
#divega answer works great and the sql output is fine but because of 'don't repeat yourself'
an extension will be a better way like
#Code Uniquely showed. But this solution can output more complex sql as you needed.
But you can use the following extension to bring both together:
public static int MaxOrZero<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, int>> selector)
{
var converted = Expression.Convert(selector.Body, typeof(int?));
var typed = Expression.Lambda<Func<TSource, int?>>(converted, selector.Parameters);
return source.Max(typed) ?? 0;
}
You can use:
FromSqlRaw("Select ifnull(max(columnname),0) as Value from tableName");
I have an entity with two fk's. I've been trying to insert a record to the database without success. This are the approaches I've used:
valuePaymentBetToAdd.BetType = db.BetTypes.First(betType => betType.Id == valuePaymentBetToAdd.BetType.Id);
valuePaymentBetToAdd.Lottery = db.Lotteries.First(lotto => lotto.Id == valuePaymentBetToAdd.Lottery.Id);
In this case the second object gets assigned but when calling the SaveChanges method I get an error saying that the properties of the Lottery object were null.
valuePaymentBetToAdd.BetTypeReference.EntityKey = new EntityKey(db.DefaultContainerName + ".BetType", "Id", valuePaymentBetToAdd.BetType.Id);
valuePaymentBetToAdd.LotteryReference.EntityKey = new EntityKey(db.DefaultContainerName + ".Lottery", "Id", valuePaymentBetToAdd.Lottery.Id);
In this case I get another weird error. When the object is being added to the collection.
The object could not be added or attached because its EntityReference has an EntityKey property value that does not match the EntityKey for this object.
Am I missing something in this case?
Try setting the EntityReference like this:
valuePaymentBetToAdd.BetTypeReference.EntityKey = b.BetTypes.First(betType => betType.Id == valuePaymentBetToAdd.BetType.Id).EntityKey;
It works for me
How about creating a stub object for BetType and Lottery where you set only the Id property, and then attach those to their respective EntitySets, and then setting these objects on you Bet object, and save - something like:
Lottery lottery = new Lottery() { Id = valuePaymentBetToAdd.Lottery.Id };
BetType betType = new BetType() { Id = valuePaymentBetToAdd.BetType.Id };
MyContext.AttachTo("Lottery", lottery);
MyContext.AttachTo("BetType", betType);
valuePaymentBetToAdd.Lottery = lottery;
valuePaymentBetToAdd.BetType = betType;
MyContext.AddToBet(valuePaymentBetToAdd);
MyContext.SaveChanges();