EF6 lazy loading: loads entire collection when adding?
up vote
1
down vote
favorite
I have the following collection navigation property on my entity:
public virtual ICollection<OrderIntegrationLogEntry> LogEntries { get; set; }
And the OrderIntegrationLogEntry entity has a little configuration:
this.HasKey(i => new {i.Id, i.IntegrationId});
this.HasRequired(i => i.Integration).WithMany(i => i.LogEntries).HasForeignKey(i => i.IntegrationId).WillCascadeOnDelete(true);
It seems that this line of code:
integration.LogEntries.Add(new OrderIntegrationLogEntry
{
Id = Guid.NewGuid(),
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
});
...results in a query which loads the contents of the collection:
SELECT [Extent1].[Id] AS [Id], [Extent1].[IntegrationId] AS [IntegrationId],
[Extent1].[CreatedUtc] AS [CreatedUtc], [Extent1].[Level] AS [Level],
[Extent1].[Message] AS [Message], [Extent1].[Detail] AS [Detail]
FROM [dbo].[OrderIntegrationLogEntries] AS [Extent1]
WHERE [Extent1].[IntegrationId] = @EntityKeyValue1
I wasn't expecting this: shouldn't it just stage an add? Do I need to configure in some other way?
entity-framework entity-framework-6
add a comment |
up vote
1
down vote
favorite
I have the following collection navigation property on my entity:
public virtual ICollection<OrderIntegrationLogEntry> LogEntries { get; set; }
And the OrderIntegrationLogEntry entity has a little configuration:
this.HasKey(i => new {i.Id, i.IntegrationId});
this.HasRequired(i => i.Integration).WithMany(i => i.LogEntries).HasForeignKey(i => i.IntegrationId).WillCascadeOnDelete(true);
It seems that this line of code:
integration.LogEntries.Add(new OrderIntegrationLogEntry
{
Id = Guid.NewGuid(),
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
});
...results in a query which loads the contents of the collection:
SELECT [Extent1].[Id] AS [Id], [Extent1].[IntegrationId] AS [IntegrationId],
[Extent1].[CreatedUtc] AS [CreatedUtc], [Extent1].[Level] AS [Level],
[Extent1].[Message] AS [Message], [Extent1].[Detail] AS [Detail]
FROM [dbo].[OrderIntegrationLogEntries] AS [Extent1]
WHERE [Extent1].[IntegrationId] = @EntityKeyValue1
I wasn't expecting this: shouldn't it just stage an add? Do I need to configure in some other way?
entity-framework entity-framework-6
Lazy loading is triggered by a property getter. Andintegration.LogEntries.Add(…)includes property getterintegration.LogEntries.
– Ivan Stoev
Nov 10 at 10:28
if you don't want lazy loading to happen, either disable it during the add procedure, do not track the item or attach the navigation property items to the sets and set their navigation property/FK to the correct values instead.
– DevilSuichiro
Nov 10 at 12:04
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have the following collection navigation property on my entity:
public virtual ICollection<OrderIntegrationLogEntry> LogEntries { get; set; }
And the OrderIntegrationLogEntry entity has a little configuration:
this.HasKey(i => new {i.Id, i.IntegrationId});
this.HasRequired(i => i.Integration).WithMany(i => i.LogEntries).HasForeignKey(i => i.IntegrationId).WillCascadeOnDelete(true);
It seems that this line of code:
integration.LogEntries.Add(new OrderIntegrationLogEntry
{
Id = Guid.NewGuid(),
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
});
...results in a query which loads the contents of the collection:
SELECT [Extent1].[Id] AS [Id], [Extent1].[IntegrationId] AS [IntegrationId],
[Extent1].[CreatedUtc] AS [CreatedUtc], [Extent1].[Level] AS [Level],
[Extent1].[Message] AS [Message], [Extent1].[Detail] AS [Detail]
FROM [dbo].[OrderIntegrationLogEntries] AS [Extent1]
WHERE [Extent1].[IntegrationId] = @EntityKeyValue1
I wasn't expecting this: shouldn't it just stage an add? Do I need to configure in some other way?
entity-framework entity-framework-6
I have the following collection navigation property on my entity:
public virtual ICollection<OrderIntegrationLogEntry> LogEntries { get; set; }
And the OrderIntegrationLogEntry entity has a little configuration:
this.HasKey(i => new {i.Id, i.IntegrationId});
this.HasRequired(i => i.Integration).WithMany(i => i.LogEntries).HasForeignKey(i => i.IntegrationId).WillCascadeOnDelete(true);
It seems that this line of code:
integration.LogEntries.Add(new OrderIntegrationLogEntry
{
Id = Guid.NewGuid(),
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
});
...results in a query which loads the contents of the collection:
SELECT [Extent1].[Id] AS [Id], [Extent1].[IntegrationId] AS [IntegrationId],
[Extent1].[CreatedUtc] AS [CreatedUtc], [Extent1].[Level] AS [Level],
[Extent1].[Message] AS [Message], [Extent1].[Detail] AS [Detail]
FROM [dbo].[OrderIntegrationLogEntries] AS [Extent1]
WHERE [Extent1].[IntegrationId] = @EntityKeyValue1
I wasn't expecting this: shouldn't it just stage an add? Do I need to configure in some other way?
entity-framework entity-framework-6
entity-framework entity-framework-6
asked Nov 10 at 8:02
Kieren Johnstone
31.5k971118
31.5k971118
Lazy loading is triggered by a property getter. Andintegration.LogEntries.Add(…)includes property getterintegration.LogEntries.
– Ivan Stoev
Nov 10 at 10:28
if you don't want lazy loading to happen, either disable it during the add procedure, do not track the item or attach the navigation property items to the sets and set their navigation property/FK to the correct values instead.
– DevilSuichiro
Nov 10 at 12:04
add a comment |
Lazy loading is triggered by a property getter. Andintegration.LogEntries.Add(…)includes property getterintegration.LogEntries.
– Ivan Stoev
Nov 10 at 10:28
if you don't want lazy loading to happen, either disable it during the add procedure, do not track the item or attach the navigation property items to the sets and set their navigation property/FK to the correct values instead.
– DevilSuichiro
Nov 10 at 12:04
Lazy loading is triggered by a property getter. And
integration.LogEntries.Add(…) includes property getter integration.LogEntries.– Ivan Stoev
Nov 10 at 10:28
Lazy loading is triggered by a property getter. And
integration.LogEntries.Add(…) includes property getter integration.LogEntries.– Ivan Stoev
Nov 10 at 10:28
if you don't want lazy loading to happen, either disable it during the add procedure, do not track the item or attach the navigation property items to the sets and set their navigation property/FK to the correct values instead.
– DevilSuichiro
Nov 10 at 12:04
if you don't want lazy loading to happen, either disable it during the add procedure, do not track the item or attach the navigation property items to the sets and set their navigation property/FK to the correct values instead.
– DevilSuichiro
Nov 10 at 12:04
add a comment |
2 Answers
2
active
oldest
votes
up vote
1
down vote
accepted
As noted by Ivan you've called the LogEntries getter, which causes Lazy Loading.
If you don't want to turn off Lazy Loading, then instead of adding the log entity to the navigation property of the parent, just set the IntegrationId of the new entitiy and SaveChanges(). eg
var entry = new OrderIntegrationLogEntry()
{
Id = Guid.NewGuid(),
IntegrationId = integration.Id,
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
);
db.OrderIntegrationLogEntries.Add(entry);
db.SaveChanges();
Also if this is SQL Server (and probably other platforms too) use sequential GUID generation. Inserting a row with a random guid as its leading key column is needlessly expensive. For SQL Server you can generate sequential GUIDs in the database with the NEWSEQUENTIALID() function as a default, or on the client
public class SQLGuidUtil
{
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
public static Guid NewSequentialId()
{
Guid guid;
UuidCreateSequential(out guid);
var s = guid.ToByteArray();
var t = new byte[16];
t[3] = s[0];
t[2] = s[1];
t[1] = s[2];
t[0] = s[3];
t[5] = s[4];
t[4] = s[5];
t[7] = s[6];
t[6] = s[7];
t[8] = s[8];
t[9] = s[9];
t[10] = s[10];
t[11] = s[11];
t[12] = s[12];
t[13] = s[13];
t[14] = s[14];
t[15] = s[15];
return new Guid(t);
}
}
How to Generate Sequential GUIDs for SQL Server in .NET
You should also consider flipping the order of your key columns if you expect the log entries to be accessed primarily on an Integration-by-Integration basis. That will store the log entries for an Integration together. EG
this.HasKey(i => new {i.IntegrationId, i.Id});
If you're not on Windows you can roll your own sequential GUID generator by starting from a random GUID and incrementing the 4 low-order bytes. The GUIDs would be sequential only within an AppDomain, but that shouldn't matter much.
Something like this:
namespace NewSequentialId
{
public class SQLGuidUtil
{
static object synclock = new object();
static uint seq = 0;
static byte seed = Guid.NewGuid().ToByteArray();
public static Guid NewSequentialId()
{
uint nextVal;
byte buf;
lock (synclock)
{
nextVal = seq++;
buf = (byte)seed.Clone();
if (nextVal == 0xFFFFFFFF)
{
seed = Guid.NewGuid().ToByteArray();
seq = 0;
}
}
var seqbytes = BitConverter.GetBytes(nextVal);
if (BitConverter.IsLittleEndian)
{
buf[0] = seqbytes[3];
buf[1] = seqbytes[2];
buf[2] = seqbytes[1];
buf[3] = seqbytes[0];
}
else
{
buf[0] = seqbytes[0];
buf[1] = seqbytes[1];
buf[2] = seqbytes[2];
buf[3] = seqbytes[3];
}
return new Guid(buf);
}
}
}
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
1
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
add a comment |
up vote
0
down vote
In order to enable lazy loading, EF creates proxy-classes derived from your model. In these classes it overrides the navigation properties to implement lazy loading. This is why your navigation properties should be defined virtual so EF can override them.
When you call integration.LogEntries.Add, the getter of your LogEntries property is being called which triggers lazy loading operation.
You can temporarily disable lazy loading using the following code:
context.Configuration.LazyLoadingEnabled = false;
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
As noted by Ivan you've called the LogEntries getter, which causes Lazy Loading.
If you don't want to turn off Lazy Loading, then instead of adding the log entity to the navigation property of the parent, just set the IntegrationId of the new entitiy and SaveChanges(). eg
var entry = new OrderIntegrationLogEntry()
{
Id = Guid.NewGuid(),
IntegrationId = integration.Id,
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
);
db.OrderIntegrationLogEntries.Add(entry);
db.SaveChanges();
Also if this is SQL Server (and probably other platforms too) use sequential GUID generation. Inserting a row with a random guid as its leading key column is needlessly expensive. For SQL Server you can generate sequential GUIDs in the database with the NEWSEQUENTIALID() function as a default, or on the client
public class SQLGuidUtil
{
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
public static Guid NewSequentialId()
{
Guid guid;
UuidCreateSequential(out guid);
var s = guid.ToByteArray();
var t = new byte[16];
t[3] = s[0];
t[2] = s[1];
t[1] = s[2];
t[0] = s[3];
t[5] = s[4];
t[4] = s[5];
t[7] = s[6];
t[6] = s[7];
t[8] = s[8];
t[9] = s[9];
t[10] = s[10];
t[11] = s[11];
t[12] = s[12];
t[13] = s[13];
t[14] = s[14];
t[15] = s[15];
return new Guid(t);
}
}
How to Generate Sequential GUIDs for SQL Server in .NET
You should also consider flipping the order of your key columns if you expect the log entries to be accessed primarily on an Integration-by-Integration basis. That will store the log entries for an Integration together. EG
this.HasKey(i => new {i.IntegrationId, i.Id});
If you're not on Windows you can roll your own sequential GUID generator by starting from a random GUID and incrementing the 4 low-order bytes. The GUIDs would be sequential only within an AppDomain, but that shouldn't matter much.
Something like this:
namespace NewSequentialId
{
public class SQLGuidUtil
{
static object synclock = new object();
static uint seq = 0;
static byte seed = Guid.NewGuid().ToByteArray();
public static Guid NewSequentialId()
{
uint nextVal;
byte buf;
lock (synclock)
{
nextVal = seq++;
buf = (byte)seed.Clone();
if (nextVal == 0xFFFFFFFF)
{
seed = Guid.NewGuid().ToByteArray();
seq = 0;
}
}
var seqbytes = BitConverter.GetBytes(nextVal);
if (BitConverter.IsLittleEndian)
{
buf[0] = seqbytes[3];
buf[1] = seqbytes[2];
buf[2] = seqbytes[1];
buf[3] = seqbytes[0];
}
else
{
buf[0] = seqbytes[0];
buf[1] = seqbytes[1];
buf[2] = seqbytes[2];
buf[3] = seqbytes[3];
}
return new Guid(buf);
}
}
}
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
1
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
add a comment |
up vote
1
down vote
accepted
As noted by Ivan you've called the LogEntries getter, which causes Lazy Loading.
If you don't want to turn off Lazy Loading, then instead of adding the log entity to the navigation property of the parent, just set the IntegrationId of the new entitiy and SaveChanges(). eg
var entry = new OrderIntegrationLogEntry()
{
Id = Guid.NewGuid(),
IntegrationId = integration.Id,
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
);
db.OrderIntegrationLogEntries.Add(entry);
db.SaveChanges();
Also if this is SQL Server (and probably other platforms too) use sequential GUID generation. Inserting a row with a random guid as its leading key column is needlessly expensive. For SQL Server you can generate sequential GUIDs in the database with the NEWSEQUENTIALID() function as a default, or on the client
public class SQLGuidUtil
{
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
public static Guid NewSequentialId()
{
Guid guid;
UuidCreateSequential(out guid);
var s = guid.ToByteArray();
var t = new byte[16];
t[3] = s[0];
t[2] = s[1];
t[1] = s[2];
t[0] = s[3];
t[5] = s[4];
t[4] = s[5];
t[7] = s[6];
t[6] = s[7];
t[8] = s[8];
t[9] = s[9];
t[10] = s[10];
t[11] = s[11];
t[12] = s[12];
t[13] = s[13];
t[14] = s[14];
t[15] = s[15];
return new Guid(t);
}
}
How to Generate Sequential GUIDs for SQL Server in .NET
You should also consider flipping the order of your key columns if you expect the log entries to be accessed primarily on an Integration-by-Integration basis. That will store the log entries for an Integration together. EG
this.HasKey(i => new {i.IntegrationId, i.Id});
If you're not on Windows you can roll your own sequential GUID generator by starting from a random GUID and incrementing the 4 low-order bytes. The GUIDs would be sequential only within an AppDomain, but that shouldn't matter much.
Something like this:
namespace NewSequentialId
{
public class SQLGuidUtil
{
static object synclock = new object();
static uint seq = 0;
static byte seed = Guid.NewGuid().ToByteArray();
public static Guid NewSequentialId()
{
uint nextVal;
byte buf;
lock (synclock)
{
nextVal = seq++;
buf = (byte)seed.Clone();
if (nextVal == 0xFFFFFFFF)
{
seed = Guid.NewGuid().ToByteArray();
seq = 0;
}
}
var seqbytes = BitConverter.GetBytes(nextVal);
if (BitConverter.IsLittleEndian)
{
buf[0] = seqbytes[3];
buf[1] = seqbytes[2];
buf[2] = seqbytes[1];
buf[3] = seqbytes[0];
}
else
{
buf[0] = seqbytes[0];
buf[1] = seqbytes[1];
buf[2] = seqbytes[2];
buf[3] = seqbytes[3];
}
return new Guid(buf);
}
}
}
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
1
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
add a comment |
up vote
1
down vote
accepted
up vote
1
down vote
accepted
As noted by Ivan you've called the LogEntries getter, which causes Lazy Loading.
If you don't want to turn off Lazy Loading, then instead of adding the log entity to the navigation property of the parent, just set the IntegrationId of the new entitiy and SaveChanges(). eg
var entry = new OrderIntegrationLogEntry()
{
Id = Guid.NewGuid(),
IntegrationId = integration.Id,
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
);
db.OrderIntegrationLogEntries.Add(entry);
db.SaveChanges();
Also if this is SQL Server (and probably other platforms too) use sequential GUID generation. Inserting a row with a random guid as its leading key column is needlessly expensive. For SQL Server you can generate sequential GUIDs in the database with the NEWSEQUENTIALID() function as a default, or on the client
public class SQLGuidUtil
{
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
public static Guid NewSequentialId()
{
Guid guid;
UuidCreateSequential(out guid);
var s = guid.ToByteArray();
var t = new byte[16];
t[3] = s[0];
t[2] = s[1];
t[1] = s[2];
t[0] = s[3];
t[5] = s[4];
t[4] = s[5];
t[7] = s[6];
t[6] = s[7];
t[8] = s[8];
t[9] = s[9];
t[10] = s[10];
t[11] = s[11];
t[12] = s[12];
t[13] = s[13];
t[14] = s[14];
t[15] = s[15];
return new Guid(t);
}
}
How to Generate Sequential GUIDs for SQL Server in .NET
You should also consider flipping the order of your key columns if you expect the log entries to be accessed primarily on an Integration-by-Integration basis. That will store the log entries for an Integration together. EG
this.HasKey(i => new {i.IntegrationId, i.Id});
If you're not on Windows you can roll your own sequential GUID generator by starting from a random GUID and incrementing the 4 low-order bytes. The GUIDs would be sequential only within an AppDomain, but that shouldn't matter much.
Something like this:
namespace NewSequentialId
{
public class SQLGuidUtil
{
static object synclock = new object();
static uint seq = 0;
static byte seed = Guid.NewGuid().ToByteArray();
public static Guid NewSequentialId()
{
uint nextVal;
byte buf;
lock (synclock)
{
nextVal = seq++;
buf = (byte)seed.Clone();
if (nextVal == 0xFFFFFFFF)
{
seed = Guid.NewGuid().ToByteArray();
seq = 0;
}
}
var seqbytes = BitConverter.GetBytes(nextVal);
if (BitConverter.IsLittleEndian)
{
buf[0] = seqbytes[3];
buf[1] = seqbytes[2];
buf[2] = seqbytes[1];
buf[3] = seqbytes[0];
}
else
{
buf[0] = seqbytes[0];
buf[1] = seqbytes[1];
buf[2] = seqbytes[2];
buf[3] = seqbytes[3];
}
return new Guid(buf);
}
}
}
As noted by Ivan you've called the LogEntries getter, which causes Lazy Loading.
If you don't want to turn off Lazy Loading, then instead of adding the log entity to the navigation property of the parent, just set the IntegrationId of the new entitiy and SaveChanges(). eg
var entry = new OrderIntegrationLogEntry()
{
Id = Guid.NewGuid(),
IntegrationId = integration.Id,
CreatedUtc = DateTime.UtcNow,
Message = message,
Level = level,
Detail = detail
);
db.OrderIntegrationLogEntries.Add(entry);
db.SaveChanges();
Also if this is SQL Server (and probably other platforms too) use sequential GUID generation. Inserting a row with a random guid as its leading key column is needlessly expensive. For SQL Server you can generate sequential GUIDs in the database with the NEWSEQUENTIALID() function as a default, or on the client
public class SQLGuidUtil
{
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
public static Guid NewSequentialId()
{
Guid guid;
UuidCreateSequential(out guid);
var s = guid.ToByteArray();
var t = new byte[16];
t[3] = s[0];
t[2] = s[1];
t[1] = s[2];
t[0] = s[3];
t[5] = s[4];
t[4] = s[5];
t[7] = s[6];
t[6] = s[7];
t[8] = s[8];
t[9] = s[9];
t[10] = s[10];
t[11] = s[11];
t[12] = s[12];
t[13] = s[13];
t[14] = s[14];
t[15] = s[15];
return new Guid(t);
}
}
How to Generate Sequential GUIDs for SQL Server in .NET
You should also consider flipping the order of your key columns if you expect the log entries to be accessed primarily on an Integration-by-Integration basis. That will store the log entries for an Integration together. EG
this.HasKey(i => new {i.IntegrationId, i.Id});
If you're not on Windows you can roll your own sequential GUID generator by starting from a random GUID and incrementing the 4 low-order bytes. The GUIDs would be sequential only within an AppDomain, but that shouldn't matter much.
Something like this:
namespace NewSequentialId
{
public class SQLGuidUtil
{
static object synclock = new object();
static uint seq = 0;
static byte seed = Guid.NewGuid().ToByteArray();
public static Guid NewSequentialId()
{
uint nextVal;
byte buf;
lock (synclock)
{
nextVal = seq++;
buf = (byte)seed.Clone();
if (nextVal == 0xFFFFFFFF)
{
seed = Guid.NewGuid().ToByteArray();
seq = 0;
}
}
var seqbytes = BitConverter.GetBytes(nextVal);
if (BitConverter.IsLittleEndian)
{
buf[0] = seqbytes[3];
buf[1] = seqbytes[2];
buf[2] = seqbytes[1];
buf[3] = seqbytes[0];
}
else
{
buf[0] = seqbytes[0];
buf[1] = seqbytes[1];
buf[2] = seqbytes[2];
buf[3] = seqbytes[3];
}
return new Guid(buf);
}
}
}
edited Nov 13 at 18:14
answered Nov 10 at 13:35
David Browne - Microsoft
13.1k1524
13.1k1524
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
1
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
add a comment |
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
1
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
Thanks - I was using lazy loading to effectively hide the dependency on the context from the class which is using the parent entity. I'll have to expose it, or the add functionality, in some way to work around this, it seems. The tip with the composite keys is a great one, thanks. The sequential GUIDs - is there a .NET Core version that you know of? Also to quote MSDN there, "you should never use this UUID to identify an object that is not strictly local to your computer." I'm looking for a decent strategy moving forward - in this case, we should really be using regular int identity..
– Kieren Johnstone
Nov 10 at 16:27
1
1
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
You can use client-side sequential GUID generation from .NET Core, but only on Windows. SQL Server has a Linux implementation of UUIDCreateSequential, but AFAIK there's no other. The warning on MSDN for sequential GUIDs is for machines without unique MAC addresses. Even then it's unlikely that you would actually get a duplicate value generated.
– David Browne - Microsoft
Nov 10 at 17:08
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
I wonder how dangerous a "home grown" solution could be: for each process, create a new completely-random GUID, possibly zero out the last few bytes, then increment from there. Each process would have its own sequential range, which looks similar to the UuidCreateSequential solution?
– Kieren Johnstone
Nov 13 at 12:43
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
That's a good idea. That pattern is sometimes called a "combination guid", and you can do it easily. See edit.
– David Browne - Microsoft
Nov 13 at 18:15
add a comment |
up vote
0
down vote
In order to enable lazy loading, EF creates proxy-classes derived from your model. In these classes it overrides the navigation properties to implement lazy loading. This is why your navigation properties should be defined virtual so EF can override them.
When you call integration.LogEntries.Add, the getter of your LogEntries property is being called which triggers lazy loading operation.
You can temporarily disable lazy loading using the following code:
context.Configuration.LazyLoadingEnabled = false;
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
add a comment |
up vote
0
down vote
In order to enable lazy loading, EF creates proxy-classes derived from your model. In these classes it overrides the navigation properties to implement lazy loading. This is why your navigation properties should be defined virtual so EF can override them.
When you call integration.LogEntries.Add, the getter of your LogEntries property is being called which triggers lazy loading operation.
You can temporarily disable lazy loading using the following code:
context.Configuration.LazyLoadingEnabled = false;
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
add a comment |
up vote
0
down vote
up vote
0
down vote
In order to enable lazy loading, EF creates proxy-classes derived from your model. In these classes it overrides the navigation properties to implement lazy loading. This is why your navigation properties should be defined virtual so EF can override them.
When you call integration.LogEntries.Add, the getter of your LogEntries property is being called which triggers lazy loading operation.
You can temporarily disable lazy loading using the following code:
context.Configuration.LazyLoadingEnabled = false;
In order to enable lazy loading, EF creates proxy-classes derived from your model. In these classes it overrides the navigation properties to implement lazy loading. This is why your navigation properties should be defined virtual so EF can override them.
When you call integration.LogEntries.Add, the getter of your LogEntries property is being called which triggers lazy loading operation.
You can temporarily disable lazy loading using the following code:
context.Configuration.LazyLoadingEnabled = false;
edited Nov 10 at 8:16
answered Nov 10 at 8:11
Kamyar
15.2k879152
15.2k879152
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
add a comment |
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Humm, I do want lazy-loading. I just don't want it when I'm adding?
– Kieren Johnstone
Nov 10 at 8:18
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
Can't think of any workaround since it's all done under the hood without letting the programmer to change the behavior. Couldn't you turn lazy loading off just for this context's instance?
– Kamyar
Nov 10 at 8:21
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53237107%2fef6-lazy-loading-loads-entire-collection-when-adding%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Lazy loading is triggered by a property getter. And
integration.LogEntries.Add(…)includes property getterintegration.LogEntries.– Ivan Stoev
Nov 10 at 10:28
if you don't want lazy loading to happen, either disable it during the add procedure, do not track the item or attach the navigation property items to the sets and set their navigation property/FK to the correct values instead.
– DevilSuichiro
Nov 10 at 12:04