I'm importing data from a delimited file using LINQ and Entity Framework and in one scenario EF is not populating the key property. The class is:
public class BallastType
{
public int BallastTypeId { get; set; }
public string Name { get; set; }
}
The file contains a header row and a row for each entry. The code to import it is:
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var seedDataPath = Path.Combine(baseDir, "bin", "SeedData");
var ballastTypesFile = Path.Combine(seedDataPath, "BallastTypes.txt");
var ballastTypes = (from l in File.ReadAllLines(ballastTypesFile).Skip(1)
let x = l.Split('\t').ToArray()
select new BallastType()
{
Name = x[0]
});
context.BallastTypes.AddRange(ballastTypes);
context.SaveChanges();
Running this code inserts all the entities but BallastTypeId is uninitialized (0). If I add ToList after the select...
...select new BallastType()
{
Name = x[0]
}).ToList();
it works as expected and BallastTypeId is populated. One note is that I'm checking this at a breakpoint.
Why does adding ToList cause EF to work as expected? I'm using EF 6.1.3.
Without a .ToList(), ballastTypes is an IQueryable, and is only evaluated when you enumerate it. If you check the values of BallastTypeId after SaveChanges, you are in essence re-running your LINQ statement, and re-reading the contents of the file. This means that the entities you read when looking for the inserted ID are not the ones you inserted into the context.
If you add a .ToList(), then ballastTypes becomes a list instead of an IQueryable, and can be evaluated multiple times.
Related
I wonder if there is an easy way using Linq to SQL with Entity Framework Core to query check if a given list of ids exist in the database and which returns the list of ids that do not exist.
The use case I come across this is if the user can do something with a list of object (represented through the list of their ids) I want to check if these ids exist or not.
Of course I could query all objects/object ids that exist in the database and cross check in a second step.
Just wondering if it would be possible in one step.
What I mean in code:
public class MyDbObject
{
public int Id { get; set; }
public string Name { get; set; }
}
public IActionResult DoSomethingWithObjects([FromQuery]List<int> ids}
{
List<int> idsThatDoNotExistInTheDb = DbContext.MyDbObject.Where(???)
return NotFound("those ids do not exist: " + string.Join(", ", idsThatDoNotExist));
}
You can obtain the list of IDs that match, then remove them from the original list, like this:
var validIds = DbContext
.MyDbObject
.Where(obj => ids.Contains(obj.Id))
.Select(obj => obj.Id);
var idsThatDoNotExistInTheDb = ids.Except(validIds);
This approach may be slow, though, so you may be better off doing it in a stored procedure that takes a table-valued parameter (how?)
Note: Pre-checks of this kind are not bullet-proof, because a change may happen between the moment when you validate IDs and the moment when you start the operation. It is better to structure your APIs in a way that it validates and then does whatever it needs to do right away. If validation fails, the API returns a list of errors.
couldn't think of any better title for my question, but the situation is really simple: I have my database (created using code first migration), I have my entity, it has two fields that has index contraint so I cannot insert duplicate records when those fields are same.
public class Entity
{
public int Id;
[Index("IndexName"), IsUnique = true, Order = 0]
[MaxLength(50)]
public string Name;
[Index("IndexName"), IsUnique = true, Order = 1]
public Guid CustomId;
}
For a short entity class example I made this (mine looks with more properties, getters, setters ;) )
I try to make this:
<...>
using (var ctx = new EntitiesDbContext())
{
var e1 = new Entity()
{
Name = "Bob",
CustomId = new Guid("068462F1-3557-E711-BA31-028037EC0200")
};
var e2 = new Entity()
{
Name = "Bob",
CustomId = new Guid("068462F1-3557-E711-BA31-028037EC0200")
};
ctx.Entities.Add(e1);
ctx.Entities.Add(e2);
await ctx.SaveChangesAsync(); // I get exception
}
<...>
Everything is fine, I cannot insert two records because of index, but my problem is, that it does not insert any value at all. Is it possible to make this situation, to add at least one value to DB (i.e. e1 object) ?
P.s. The problem came from more complex situation, my example is pretty obvious or stupid, but it shows the idea what I want to achieve. The problem was the system performance I guess, when two records were inserted into context and then when I context tried to save it, I got an exception.
In all versions of Entity Framework, whenever you execute
SaveChanges() to insert, update or delete on the database the
framework will wrap that operation in a transaction.
Source: https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx
So you have to save every item seperatly because the behaviour of a transaction is to do no changes to your database in case of an error.
ctx.Entities.Add(e1);
ctx.SaveChanges();
ctx.Entities.Add(e2);
ctx.SaveChanges();
What I found interesting, I can check ctx.ChangesTracker object to get entities list that are changed.
So I just did simple check for changes count and if the count was 2 for example, I made those entities state to Unchanged.
ctx.ChangeTracker.Entries().First().State = EntityState.Unchanged
This solved my problem and pain that I was dealing with for several hours.
Cross posted on MSDN
We use manual code first Entity Framework for System.Data.Sqlite
So when creating a new entity, we manually create the table, the c# object, and add the DbSet to our context.
I have a very simple object created and I am getting this error when trying to query the context. I have checked the column names and data types many times but I don't see a mismatch here. Also there are no foreign key relationships defined even though the fields have id in the name. Just a standalone table.
The weirdest part is that I can add a new entity to the context, save changes, and it will be persisted to the db. However in the next line when trying to retrieve the entities I get The entity set is not defined in the entity container error. I have also noticed that if I hover over an instantiated context, all the other db sets will have the EF SQL such as SELECT EXTENT1.myCol as myCol, but the department_resources set just says {System.Data.Entity.DbSet<department_resource>}.
Any ideas on what the issue is here?
Below are excerpts of my files:
DDL
CREATE TABLE department_resources (
dep_res_key VARCHAR PRIMARY KEY,
department_id INT NOT NULL,
resource_id INT NOT NULL);
department_resource.cs
[Table("department_resources")]
public class department_resource
{
[Key]
public string dep_res_key { get; set; }
public int department_id { get; set; }
public int resource_id { get; set; }
}
MyContext.cs
public class MyContext : DbContext
{
public DbSet<department_resource> department_resources { get; set; }
}
Sample Usage
using (MyContext db = new MyContext())
{
db.department_resources.Add(new department_resource()
{ dep_res_key = "anID",
resource_id = 22,
department_id = 23 }); // Works
db.SaveChanges(); // Also works. Even persists to db
var foo = from r in db.department_resources
select r.resource_id; // Doesn't work. Will error as soon as I try to use foo. Like assigning to a combo box item source. Or even just enumerating the results
var bar = db.department_resources; // Also doesn't work.
}
The issue is with deferred execution. Although you're assigning foo and bar within your using block, they're not getting evaluated until they're actually used, after MyContext has been disposed.
You need to force them to evaluated within your using block. e.g. by converting the results to lists.
Also I notice you've declared them as vars within your using block. They'll need to be defined outside of it to be able to use them outside (perhaps you just did this in your sample to simplify?)
List<int> foo;
List<department_resource> bar;
using (MyContext db = new MyContext())
{
db.department_resources.Add(new department_resource()
{ dep_res_key = "anID",
resource_id = 22,
department_id = 23 }); // Works
db.SaveChanges(); // Also works. Even persists to db
foo = (from r in db.department_resources
select r.resource_id).ToList();
bar = db.department_resources.ToList();
}
From MSDN
the query variable itself only stores the query commands. The actual
execution of the query is deferred until you iterate over the query
variable in a foreach statement. This concept is referred to as
deferred execution
Forcing Immediate Execution
Queries that perform aggregation functions over a range of source elements must first iterate over
those elements. Examples of such queries are Count, Max, Average, and
First. These execute without an explicit foreach statement because the
query itself must use foreach in order to return a result. Note also
that these types of queries return a single value, not an IEnumerable
collection.
You can also force execution by putting the foreach loop immediately
after the query expression. However, by calling ToList or ToArray you
also cache all the data in a single collection object.
I have two tables Studies and Series. Series are FK'd back to Studies so one Study contains a variable number of Series.
Each Series item has a Deleted column indicating it has been logically deleted from the database.
I am trying to implement a Deleted property in the Study class that returns true only if all the contained Series are deleted.
I am using O/R Designer generated classes, so I added the following to the user modifiable partial class for the Study type:
public bool Deleted
{
get
{
var nonDeletedSeries = from s in Series
where !s.Deleted
select s;
return nonDeletedSeries.Count() == 0;
}
set
{
foreach (var series in Series)
{
series.Deleted = value;
}
}
}
This gives an exception "The member 'PiccoloDatabase.Study.Deleted' has no supported translation to SQL." when this simple query is executed that invokes get:
IQueryable<Study> dataQuery = dbCtxt.Studies;
dataQuery = dataQuery.Where((s) => !s.Deleted);
foreach (var study in dataQuery)
{
...
}
Based on this http://www.foliotek.com/devblog/using-custom-properties-inside-linq-to-sql-queries/, I tried the following approach:
static Expression<Func<Study, bool>> DeletedExpr = t => false;
public bool Deleted
{
get
{
var nameFunc = DeletedExpr.Compile();
return nameFunc(this);
}
set
{ ... same as before
}
}
I get the same exception when a query is run that there is no supported translation to SQL. (
The logic of the lambda expression is irrelevant yet - just trying to get past the exception.)
Am I missing some fundamental property or something to allow translation to SQL? I've read most of the posts on SO about this exception, but nothing seems to fit my case exactly.
I believe the point of LINQ-to-SQL is that your entities are mapped for you and must have correlations in the database. It appears that you are trying to mix the LINQ-to-Objects and LINQ-to-SQL.
If the Series table has a Deleted field in the database, and the Study table does not but you would like to translate logical Study.Deleted into SQL, then extension would be a way to go.
public static class StudyExtensions
{
public static IQueryable<study> AllDeleted(this IQueryable<study> studies)
{
return studies.Where(study => !study.series.Any(series => !series.deleted));
}
}
class Program
{
public static void Main()
{
DBDataContext db = new DBDataContext();
db.Log = Console.Out;
var deletedStudies =
from study in db.studies.AllDeleted()
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
}
}
This maps your "deleted study" expression into SQL:
SELECT t0.study_id, t0.name
FROM study AS t0
WHERE NOT EXISTS(
SELECT NULL AS EMPTY
FROM series AS t1
WHERE (NOT (t1.deleted = 1)) AND (t1.fk_study_id = t0.study_id)
)
Alternatively you could build actual expressions and inject them into your query, but that is an overkill.
If however, neither Series nor Study has the Deleted field in the database, but only in memory, then you need to first convert your query to IEnumerable and only then access the Deleted property. However doing so would transfer records into memory before applying the predicate and could potentially be expensive. I.e.
var deletedStudies =
from study in db.studies.ToList()
where study.Deleted
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
When you make your query, you will want to use the statically defined Expression, not the property.
Effectively, instead of:
dataQuery = dataQuery.Where((s) => !s.Deleted);
Whenever you are making a Linq to SQL query, you will instead want to use:
dataQuery = dataQuery.Where(DeletedExpr);
Note that this will require that you can see DeletedExpr from dataQuery, so you will either need to move it out of your class, or expose it (i.e. make it public, in which case you would access it via the class definition: Series.DeletedExpr).
Also, an Expression is limited in that it cannot have a function body. So, DeletedExpr might look something like:
public static Expression<Func<Study, bool>> DeletedExpr = s => s.Series.Any(se => se.Deleted);
The property is added simply for convenience, so that you can also use it as a part of your code objects without needing to duplicate the code, i.e.
var s = new Study();
if (s.Deleted)
...
I have this query in sql and it works fine:
update userinfo set Interest = 0.98 where userid = 313
And I want to do it in linq, so I prepared the following:
public class TableDataDTO
{
public string Columnname { get; set; }
public string Value { get; set; }
public Type DataType { get; set; }
}
Implementation:
TableDataDTO tableData = new TableDataDTO();
tableData.Columnname = "Interest";
tableData.Value = "0.98";
using (dbase instance = new dbase())
{
string predicate = string.Format("it.UserID=={0} set it.{1}={2}" ,
313, tableData.Columnname, tableData.Value);
var uinfo = instance.userinfoes.Where(predicate).FirstOrDefault();
if (uinfo != null)
{
instance.SaveChanges();
return true;
}
}
But it gives me this error:
The query syntax is not valid. Near keyword 'SET'
I will be dealing with different columns, so I need to use linq predicates to minimize the code.
I don't like using any plugins to make this. Hope someone could help.
Edit
I think what I mean is "How to update data in using Dynamic linq"
Edit2
So this is the real scenario. Users/Client can update their information, e.g. First name, Last name, Address, City.. etc.. not at once but capable of updating the info one by one.
So what does it mean? Ok I can create a method that can update the First Name, next is the Last Name, Address and so one.. But if I do this, it will consume a lot of code. If only if there is a code in linq that can do what SQL does in updating data, then I would just need a code that gets the column name and set its value. Hope I'd explain it well.
Edit3
I have changed the question from How to update data in linq using predicates? to How to update column data using sql query in linq? for I misunderstood the real meaning of predicate.
Your predicate should just be the where part of the query (a predicate just returns true or false). Try this:
instance.userinfoes.Where(user => user.userid == 313).First().Interest = 0.98;
You can structure LINQ similar to how you'd structure SQL. Through a combination of Where and ForEach you should be able to update all the rows you need. I.e:
instance.userinfoes.Where(it => it.UserId == 313).ToList()
.ForEach(
it => it.Interest = 0.98M
);
There's not really any way to write SQL-like queries as text and pass them to regular LINQ as far as I know.
See this question for more solutions:
Update all objects in a collection using LINQ