I am using a database-first approach with Entity Framework in .NET Core which ends up using this. Some tables are with a relationship in SQL Server which results joined in code.
I was about to get a DateTime on a joined table and want to give a string format, but to do it I should execute first the query so I could give a format to it like what was mentioned here. The question is, why did the joined table go null after the execution?
To explain my problem easily here's the code:
var a = (from p in db.Product
select s).ToList();
var b = (from p in a
select p.Brand.DateCreated.ToString("MMMM dd, yyyy")).ToList();
// Brand contains null after execution in "a"
var c = (from p in db.Product
select p.Brand.DateCreated.ToString("MMMM dd, yyyy")).ToList();
// Brand contains data but execution gives error due to DateTime.ToString()
In the meantime, This is my solution but I need to create another Model. Which will end up a lot of Model just for this case.
public class ProductBrand
{
public class Product { get; set; }
public class Brand { get; set; }
}
var a = (from s in db.Product
select new ProductBrand {
Product = s,
Brand = s.Brand,
}).ToList().Select(s => s.Brand.DateCreated.ToString("MMMM dd, yyyy")).ToList();
EDITTED:
Above problem was solved by just updating my Model. But problem related to above question still encounter. When saving new Product the Brand becomes null after saving
To explain my problem easily here's the code:
var p = new Product { Name = "temp", BrandId = 1 };
db.Product.Add(p);
db.SaveChanged();
return p.Brand.DateCreated.ToString("MMMM dd, yyyy"); // but Brand is null
In the meantime
var p = new Product { Name = "temp", BrandId = 1 };
db.Product.Add(p);
db.SaveChanged();
if(p.Brand == null) { // Here's my temporary solution, but how about for those tables with lot of joins
p.Brand = db.Brand.FirstOrDefault(s => s.Id == p.BrandId);
}
return p.Brand.DateCreated.ToString("MMMM dd, yyyy");
Did I miss something?
I have a database with a table called Articles.
The Table stores Articles and has a field CDTimeStamp.
The CDTimestamp field got altered like this, so that it always has the correct creation date:
ALTER TABLE [dbo].[Artikel] ADD CONSTRAINT [DF_Artikel_CDTimeStamp] DEFAULT (getdate()) FOR [CDTimeStamp]
GO
So if I try to add an article, I get an error.
The article is added like this:
public void AddArticle()
{
this.Open();
Article article = new Article();
article.Description = "";
article.ArticleNr = GetArticleNumber();
article.Barcode = GetBarcode(); //EAN
article.Branch = GetBranch(); // 3digit number
article.Company = GetCompany(); // 1 or 2
article.Preis = GetPrice();
article.PreisNew = GetNewPrice();
//article.CDTimeStamp = DateTime.Now;
_OutDataContext.Artikel.InsertOnSubmit(article);
try
{
this.Submit();
}
catch (Exception e)
{
throw;
}
this.Close();
}
The error I get is:
SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and
12/31/9999 11:59:59 PM.
If I uncomment //article.CDTimeStamp = DateTime.Now; a DateTime is created and inserted but the getdate() default value should be inserted and not the value I create in my program.
My question is:
Is there a configuration entry or something alike that enables the calling of the default function ? The database field may not be null.
P.S.
I wasn't quite sure how to call this question so please feel free to edit it if you know a more correct title.
I think ColumnAttribute.IsDbGenerated is what you are looking for.
In class mapping use something like:
class Article
{
[Column(..., IsDbGenerated = true)]
public DateTime CDTimeStamp { get; set; }
...
I'd like to know how can I search for empty strings when I'm using a text type field with Entity Framework.
I've looked the SQL query that Entity is generating and It's using LIKE to compare because It's searching in a text type field, so when I use .Equals(""), == "", == string.Empty, .Contains(""), .Contains(string.Empty), and everything else, It's returning all results because it sql query is like '' and the == command throws exception because It uses the = command that is not valid with text type field.
When I try to use .Equals(null), .Contains(null), == null, It returns nothing, because It is generating FIELD ISNULL command.
I already tried the .Lenght == 0 but It throws an exception...
This works for me:
public class POCO
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
static void Main(string[] args)
{
var pocos = new List<POCO>
{
new POCO { Id = 1, Name = "John", Description = "basic" },
new POCO { Id = 2, Name = "Jane", Description = "" },
new POCO { Id = 3, Name = "Joey", Description = string.Empty }
};
pocos.Where(x => x.Description == string.Empty)
.ToList()
.ForEach(x => Console.WriteLine($"{x.Id} {x.Name} {x.Description}"));
}
However the issue MAY BE that your T4 generated object is not fully realized with data you can use, if you are using Entity Framework. EG the translation from the database is not populating objects to interrogate correctly. I would just do an operation like this to see:
using (var context = new YOURCONTEXTNAME())
{
var persons = context.YOURDATABASEOBJECT.ToList();
persons.ForEach(x => Console.WriteLine($"{x.COLUMNINQUESTION}"));
}
If you are successfully having data in it, it should be retrieved. I would not use text if possible. Use a varchar(max) nvarchar(max) xml, whatever text will be deprecated eventually and is bad form so to speak to continue using at this point.
EDIT
Okay I see, the answer is you cannot interogate the object until it is fully realized when it is text. I did a test on my local database and created a context and tested it and sure enough you cannot do a '== string.empty', '== ""', or 'String.IsNullOrEmpty()' on a text. However you can do it once the object is materialized in a realized object. EG:
// Won't work as context does not understand type
//var persons = context.tePersons.Where(x => x.Description == string.Empty).ToList();
//Works fine as transformation got the object translated to a string in .NET
var start = context.tePersons.ToList();
var persons = start.Where(x => x.Description == String.Empty).ToList();
This poses a problem obviously as you need to get ALL your data potentially before performing a predicate. Not the best means by any measure. You could do a sql object for this instead then to do a function, proc, or view to change this.
Today I've found and an exception in my code.
DbUpdateConcurrencyException
It was very confusing because there were no changes in business logic and there was no updating of entities. After some hours of trying to understand I decided to turn on SQL Server profiler.
And here is an example of issue that I found.
Lets look at Foo entity definition:
public class Foo
{
public Foo()
{
}
public Foo(int someId, DateTime createdAt, int x, int y)
{
SomeId = someId;
CreatedAt = createdAt;
X = x;
Y = y;
}
public int SomeId { get; private set; }
public DateTime CreatedAt { get; private set; }
public int X { get; private set; }
public int Y { get; private set; }
}
If you try to add an instance of Foo and save db context it will work.
using (var myDbContext = new MyDbContext())
{
DateTime now = DateTime.UtcNow;
var foo = new Foo(1, now, 2, 2);
myDbContext.Set<Foo>().Add(foo);
myDbContext.SaveChanges();
}
Below is code that is executed when you save changes.
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (#0, #1, #2, #3)
',N'#0 int,#1 datetime2(7),#2 int,#3 int',#0=1,#1='2015-10-15 12:45:15.2580302',#2=2,#3=2
Now lets add a computed column.
public int Sum { get; private set; }
Bellow is code for creation column in migration.
Sql("alter table dbo.Foo add Sum as (X + Y)");
Update the database. Now the code throws DbUpdateConcurrencyException. And here is why. If we look in SQL Server profiler, we'll see the code above:
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (#0, #1, #2, #3)
SELECT [Sum]
FROM [dbo].[Foo]
WHERE ##ROWCOUNT > 0 AND [SomeId] = #0 AND [CreatedAt] = #1',N'#0 int,#1 datetime2(7),#2 int,#3 int',#0=1,#1='2015-10-15 12:55:29.4479727',#2=2,#3=2
Now the query returns [Sum] column because it is computed. Insertion works well. But result set is empty. I think it causes DbUpdateConcurrencyException. The problem is in type of #1 variable. It is datetime2(7), but if you look in type of CreatedAt column you'll see that it is datetime. If you execute the script above, you’ll find a new row with '2015-10-15 12:55:29.447' in CreatedAt column (cast works fine). But query tries to find [Sum] of the row where CreatedAt equals to '2015-10-15 12:55:29.4479727'. Of cause the result set is empty.
So, you can fix the problem in two ways:
Change the precision of column value (for example without milliseconds).
Set manually the type of CreatedAt column in migration (datetime2(7)).
In my case I select first because I don't want to change database schema.
Here is the project to reproduce the problem.
PS: Sorry for my English:)
facing quite a similar case I use the following extension method
public static DateTime RoundedToSeconds(this DateTime dt) {
return new DateTime(dt.Ticks - (dt.Ticks % TimeSpan.TicksPerSecond), dt.Kind);
}
then your code should be
public Foo(int someId, DateTime createdAt, int x, int y)
{
SomeId = someId;
CreatedAt = createdAt.RoundedToSeconds();
X = x;
Y = y;
}
IMHO you may use a rounding to ms.
This issue doesn't repro with current builds of Entity Framework Core. The parameter type for the CreatedAt becomes of type DateTime2.
I think you missed mentioning that the key of the entity contains both SomeId and CreatedAt.
And here is an example of issue that I found.
Lets look at Foo entity definition:
public class Foo
{
public Foo()
{
}
public Foo(int someId, DateTime createdAt, int x, int y)
{
SomeId = someId;
CreatedAt = createdAt;
X = x;
Y = y;
}
public int SomeId { get; private set; }
public DateTime CreatedAt { get; private set; }
public int X { get; private set; }
public int Y { get; private set; }
}
If you try to add an instance of Foo and save db context it will work.
using (var myDbContext = new MyDbContext())
{
DateTime now = DateTime.UtcNow;
var foo = new Foo(1, now, 2, 2);
myDbContext.Set<Foo>().Add(foo);
myDbContext.SaveChanges();
}
Below is code that is executed when you save changes.
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (#0, #1, #2, #3)
',N'#0 int,#1 datetime2(7),#2 int,#3 int',#0=1,#1='2015-10-15 12:45:15.2580302',#2=2,#3=2
Now lets add a computed column.
public int Sum { get; private set; }
Bellow is code for creation column in migration.
Sql("alter table dbo.Foo add Sum as (X + Y)");
Update the database. Now the code throws DbUpdateConcurrencyException. And here is why. If we look in SQL Server profiler, we'll see the code above:
exec sp_executesql N'INSERT [dbo].[Foo]([SomeId], [CreatedAt], [X], [Y])
VALUES (#0, #1, #2, #3)
SELECT [Sum]
FROM [dbo].[Foo]
WHERE ##ROWCOUNT > 0 AND [SomeId] = #0 AND [CreatedAt] = #1',N'#0 int,#1 datetime2(7),#2 int,#3 int',#0=1,#1='2015-10-15 12:55:29.4479727',#2=2,#3=2
Now the query returns [Sum] column because it is computed. Insertion works well. But result set is empty. I think it causes DbUpdateConcurrencyException. The problem is in type of #1 variable. It is datetime2(7), but if you look in type of CreatedAt column you'll see that it is datetime. If you execute the script above, you’ll find a new row with '2015-10-15 12:55:29.447' in CreatedAt column (cast works fine). But query tries to find [Sum] of the row where CreatedAt equals to '2015-10-15 12:55:29.4479727'. Of cause the result set is empty.
So, you can fix the problem in two ways:
Change the precision of column value (for example without milliseconds).
Set manually the type of CreatedAt column in migration (datetime2(7)).
In my case I select first because I don't want to change database schema.
Here is the project to reproduce the problem.
PS: Sorry for my English:)
If I had a User model that looks like this:
public class User
{
public Guid Id { get; set; }
public DateTime CreatedAtUtc { get; set; }
public string Username { get; set; }
public string Country { get; set; }
}
...and I'd perform a query which starts at a given row and fetches a limited amount of further rows (basically for paginating over the results) that looks like this:
var spaniards = connection.Query<User>(
"select * from Users where Country=#Country OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY",
new { Country = "Spain" }).ToList();
.. using Dapper(.Net), would it be possible to get that particular, limited result set AND the total count of rows in one single query and if so.. how?
One way to do solve this is using the splitOn functionality, together with count(1) over()
Let's assume the following sql string:
var sql = "select *, overall_count = COUNT(1) OVER() from Users ORDER BY Id Asc OFFSET 5 ROWS;"
together with the following dapper.net call:
HashSet<int> hashSet = new HashSet<int>();
Func<User, int, User> map = (result, count) =>
{
hashSet.Add(count);
return result;
};
await connection.QueryAsync(sql, map, "overall_count").ConfigureAwait(false);
This will split the result of the dapper call into two parts (just like with joining different tables) and it will call the map functor each time, in this case we simply store the count in a hashset (you could then check if the hashSet.Count is 1)
Hope this helps
PS: I'm not certain that your OFFSET works without any ORDER BY