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?










share|improve this question






















  • 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















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?










share|improve this question






















  • 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













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?










share|improve this question













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






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 10 at 8:02









Kieren Johnstone

31.5k971118




31.5k971118












  • 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


















  • 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
















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












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);
}
}
}





share|improve this answer























  • 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


















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;





share|improve this answer























  • 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













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















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

























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);
}
}
}





share|improve this answer























  • 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















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);
}
}
}





share|improve this answer























  • 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













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);
}
}
}





share|improve this answer














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);
}
}
}






share|improve this answer














share|improve this answer



share|improve this answer








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


















  • 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












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;





share|improve this answer























  • 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

















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;





share|improve this answer























  • 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















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;





share|improve this answer














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;






share|improve this answer














share|improve this answer



share|improve this answer








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




















  • 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




















 

draft saved


draft discarded



















































 


draft saved


draft discarded














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





















































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







Popular posts from this blog

How to pass form data using jquery Ajax to insert data in database?

National Museum of Racing and Hall of Fame

Guess what letter conforming each word