Using MSSQL with IDENTITY column for IDs,
how can I get entity IDs synchronized with table IDs after calling BulkInsert?
context.BulkInsert(entities);
Neither of both achieves the requested result:
context.BulkSynchronize(entities);
context.BulkMerge(entities);
Assume we have one entity
var newSomething = new Something { Id = 0 };
and the corresponding TSQL table column definition
ID int IDENTITY(1,1)
Entity Framework automatically sets Id after calling SaveChanges()
context.SomethingSet.Add(newSomething);
context.SaveChanges();
Assert.IsTrue(newSomething.Id != 0)
See also How can I get Id of inserted entity in Entity framework?
How does EFPlus provide a way of getting the Id of inserted entities?
Disclaimer: I'm the owner of the project Entity Framework Extensions
The Entity Framework Extensions library should by default already return the ids for inserted entities.
For example, the following code should already work and return ids when using with BulkInsert.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Windows.Forms;
namespace Z.EntityFramework.Extensions.Lab
{
public partial class Form_Request_Ids : Form
{
public Form_Request_DateNull()
{
InitializeComponent();
// CLEAR
using (var ctx = new CurrentContext())
{
ctx.EntitySimples.RemoveRange(ctx.EntitySimples);
ctx.SaveChanges();
}
// TEST
using (var ctx = new CurrentContext())
{
var list = new List<EntitySimple>();
list.Add(new EntitySimple() { Id = 0, IntColumn = 1, CreatedDate = DateTime.Now });
ctx.BulkInsert(list);
}
}
public class CurrentContext : DbContext
{
public CurrentContext()
: base("CodeFirstEntities")
{
}
public DbSet<EntitySimple> EntitySimples { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Types().Configure(x => x.ToTable(GetType().DeclaringType != null ? GetType().DeclaringType.FullName.Replace(".", "_") + "_" + x.ClrType.Name : ""));
base.OnModelCreating(modelBuilder);
}
}
public class EntitySimple
{
public int Id { get; set; }
public int IntColumn { get; set; }
public DateTime CreatedDate { get; set; }
}
}
}
If you still have the issue, try to contact us directly with an example info#zzzprojects.com or post your example here.
Related
I have properties in my Model like
public class Test{
public int Id{ get; set; }
public string Title { get; set; }
public DateTime? CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public DateTime? DeletedDate { get; set; }
}
Here, I am using this Test class for creating Table in Database using Migration,
Now the table is created successfully but the problem is when i want do any operation using stored procedure which is like " Select Title from Test where Id=1" ,When i run the this i am facing error like this
"The required column 'CreatedDate' was not present in the results of a
'FromSql' operation"
I have used
NotMapped Attribute it works fine but when i add another migration the NotMapped properties gets Dropped from the database after updating the database
Also use Shadow properties and Ignore properties like
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Test>().Property<DateTime?>("CreatedDate");
modelBuilder.Entity<Test>().Property<DateTime?>("ModifiedDate");
modelBuilder.Entity<Test>().Property<DateTime?>("DeletedDate");
}
Also try this,
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Test>().Ignore(x => x.DeletedDate);
modelBuilder.Entity<Test>().Ignore(x => x.IsDeleted);
modelBuilder.Entity<Test>().Ignore(x => x.ModifiedDate); }
But the issue remains the same ,
So the issue is i want to ignore the CreateDate, ModifiedDated, DeletedDated property while performing DB operation and also not want to drop these columns from Database when i add and update new migration.
"The required column 'CreatedDate' was not present in the results of a
'FromSql' operation"
The first thing you need to know is that the root problem of this error is not your CreatedDate field, but the type you return after executing FromSql.
When you execute FromSql, the return type is Test, and the Test type contains all fields(Id,Title,CreatedDate...), but your stored procedure only selects the Title field,therefore, the received type does not match, and this error occurs.
You can solve this problem from two methods.
The first method is to change the stored procedure to return data consistent with the Test type.
Select * from Test where Id=1
The other method changes from the perspective of receiving types.
You can customize the FromSql method to make the returned type dynamic.
public static class CustomFromSqlTest
{
public static IEnumerable<dynamic> FromSql(this DbContext dbContext, string Sql, Dictionary<string, object> Parameters)
{
using (var cmd = dbContext.Database.GetDbConnection().CreateCommand())
{
cmd.CommandText = Sql;
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
foreach (KeyValuePair<string, object> param in Parameters)
{
DbParameter dbParameter = cmd.CreateParameter();
dbParameter.ParameterName = param.Key;
dbParameter.Value = param.Value;
cmd.Parameters.Add(dbParameter);
}
//var retObject = new List<dynamic>();
using (var dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
var dataRow = GetDataRow(dataReader);
yield return dataRow;
}
}
}
}
private static dynamic GetDataRow(DbDataReader dataReader)
{
var dataRow = new ExpandoObject() as IDictionary<string, object>;
for (var fieldCount = 0; fieldCount < dataReader.FieldCount; fieldCount++)
dataRow.Add(dataReader.GetName(fieldCount), dataReader[fieldCount]);
return dataRow;
}
}
Use it:
var result = _context.FromSql("spName #Id", new Dictionary<string, object> { { "#Id", 1 } }).ToList();
Using Entity Framework 6, I was able to use execute a Raw SQL Query and use a custom model which was not defined in the DBContext in order to store the output of the query. A simple example is the following:
List<MyModel> data = context.Database.SqlQuery<MyModel>("SELECT Orders.OrderID, Customers.CustomerName FROM Orders INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;").ToList();
I execute one SQL command and I expect a list of custom models.
I try to do something similar with Entity Framework Core and the closest example that I found will force me to define a property from DBContext. This will not allow me to use a custom model to fill the data that SQL server will return.
var books = context.Books.FromSql("SELECT * FROM Books").ToList();
This query informs Entity Framework Core that the query will return a list of books. Is there a way to implement something like this in Entity Framework Core?
From .NET Core 2.1:
Add modelBuilder.Query<YourModel>() to OnModelCreating(ModelBuilder modelBuilder)
Use context.Query<YourModel>().FromSql(rawSql) to get data
Here's how I was able to get this working (for completeness):
MyModel.cs:
public class MyModel
{
// The columns your SQL will return
public double? A { get; set; }
public double? B { get; set; }
}
Add class that just inherits from your original EF context class (i called mine DbContextBase):
public class DbContext : DbContextBase
{
public virtual DbSet<MyModel> MyModels { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Necessary, since our model isnt a EF model
modelBuilder.Entity<MyModel>(entity =>
{
entity.HasNoKey();
});
}
}
Use that class (instead of your original EF context class):
// Use your new db subclass
using (var db = new DbContext())
{
var models = await db.MyModels.FromSqlRaw(...).ToListAsync(); // E.g.: "SELECT * FROM apple A JOIN banana B ON A.col = B.col"
}
Notes:
If you need to, just use FromSqlInterpolated instead of
FromSqlRaw
The "db context" subclass allows you to update EF models without affecting your "polyfill" code
Works with SQL Server stored procs that return only 1 result set
The question was about .NET Core 2. Now I have a solution and I am going to write it here so that someone else could use it in case he/she needs it.
First of all we add the following method in dbContext class
public List<T> ExecSQL<T>(string query)
{
using (var command = Database.GetDbConnection().CreateCommand())
{
command.CommandText = query;
command.CommandType = CommandType.Text;
Database.OpenConnection();
List<T> list = new List<T>();
using (var result = command.ExecuteReader())
{
T obj = default(T);
while (result.Read())
{
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
if (!object.Equals(result[prop.Name], DBNull.Value))
{
prop.SetValue(obj, result[prop.Name], null);
}
}
list.Add(obj);
}
}
Database.CloseConnection();
return list;
}
}
Now we can have the following code.
List<Customer> Customers = _context.ExecSQL<Customer>("SELECT ......");
follow these steps:
Create your model
Probably it could be better if you can reduce it to a model as generic as possible but it's not a must:
public class MyCustomModel
{
public string Text { get; set; }
public int Count { get; set; }
}
Add it to your own DbContext
Create DbSet for your custom model
public virtual DbSet<MyCustomModel> MyCustomModelName { get; set; }
Keep in mind to specify your custom model has no key
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
...
modelBuilder.Entity<MyCustomModel>().HasNoKey();
}
Use it from your dbContext instance
async public Task<List<MyCustomModel>> GetMyCustomData()
{
var rv = new List<MyCustomModel>();
using (var dataContext = new DbContext())
{
var sql = #"
select textField as 'Text', count(1) as 'Count'
from MyTable";
rv = await dataContext.Set<MyCustomModel>().FromSqlRaw(sql).ToListAsync();
}
return rv;
}
EDIT I remade an entire project for this one problem. And thus, I remade the question.
I want to be able to efficiently avoid N+1 and Cartesian joins joining together a 4 level deep entity with a composite key on the third level.
I am looking for this to be done in only a few queries, not lazy loaded, and not just join all the tables together.
A -(many)-> B -(many)-> C -(composite, single)-> D
Something like:
Select * From A Left Join B On A.Id = B.AId
Select * From B Left Join C On B.Id = C.BId Inner Join D On C.DId = D.Id
Here is the code used
This is a fully functional app.
I used NuGet to install Sqlite x86, StructureMap, NHProf, Fluent NH.
StructureMapServiceLocator:
namespace MyTest.NHibernateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using StructureMap;
public class StructureMapServiceLocator : ServiceLocatorImplBase
{
private readonly IContainer _container;
public StructureMapServiceLocator(IContainer container)
{
_container = container;
}
public IContainer Container { get { return _container; } }
protected override object DoGetInstance(Type serviceType, string key)
{
return string.IsNullOrEmpty(key)
? _container.GetInstance(serviceType)
: _container.GetInstance(serviceType, key);
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType).Cast<object>().AsEnumerable();
}
public override TService GetInstance<TService>()
{
return _container.GetInstance<TService>();
}
public override TService GetInstance<TService>(string key)
{
return _container.GetInstance<TService>(key);
}
public override IEnumerable<TService> GetAllInstances<TService>()
{
return _container.GetAllInstances<TService>();
}
}
}
AppRegistry
namespace MyTest.NHibernateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using StructureMap.Configuration.DSL;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Cfg;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
using FluentNHibernate.Automapping;
using FluentNHibernate.Data;
public class AppRegistry : Registry
{
public AppRegistry()
{
var dbConfiguration = SQLiteConfiguration.Standard
.ConnectionString("Data Source=sqlite.db;Version=3;New=True;");
dbConfiguration.ShowSql();
var cfg = Fluently.Configure()
.Database(dbConfiguration)
.Mappings(m =>
{
m.AutoMappings.Add(AutoMap.AssemblyOf<Program>().Where(t =>
{
return typeof(Entity).IsAssignableFrom(t);
}));
})
.ExposeConfiguration(c =>
{
if (RebuildSchema.Value)
new SchemaExport(c).Create(false, true);
});
var sessionFactory = cfg.BuildSessionFactory();
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<ISession>().HybridHttpOrThreadLocalScoped().Use(cx =>
{
var session = cx.GetInstance<ISessionFactory>().OpenSession();
session.FlushMode = FlushMode.Commit;
return session;
});
}
}
}
Listing Entities:
namespace MyTest.NHibernateTest.Entities
{
using System;
using System.Collections.Generic;
using System.Linq;
using FluentNHibernate.Data;
public class Listing : Entity
{
public Listing()
{
Items = new List<ListingItem>();
}
public virtual IList<ListingItem> Items { get; set; }
}
public class ListingItem : Entity
{
public ListingItem()
{
Values = new List<ListingItemValue>();
}
public virtual IList<ListingItemValue> Values { get; set; }
}
public class ListingItemValue : Entity
{
public virtual ListingItem ListingItem { get; set; }
public virtual ListingItemField ListingItemField { get; set; }
}
public class ListingItemField : Entity
{
public virtual string Value { get; set; }
}
}
Program (console):
namespace MyTest.NHibernateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using StructureMap;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using Microsoft.Practices.ServiceLocation;
using NHibernate;
using System.Threading;
using NHibernate.Transform;
using MyTest.NHibernateTest.Entities;
public static class RebuildSchema
{
public static bool Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
RebuildSchema.Value = true;
Setup();
BuildData();
Work();
Console.ReadLine();
}
static void Setup()
{
NHibernateProfiler.Initialize();
ObjectFactory.Initialize(x =>
{
x.Scan(s =>
{
s.TheCallingAssembly();
s.LookForRegistries();
});
});
ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(ObjectFactory.Container));
}
static void BuildData()
{
var s = ObjectFactory.GetInstance<NHibernate.ISession>();
using (var t = s.BeginTransaction())
{
var listing = new Listing();
s.Save(listing);
var item = new ListingItem();
listing.Items.Add(item);
s.Save(item);
var item2 = new ListingItem();
listing.Items.Add(item2);
s.Save(item2);
var field = new ListingItemField();
field.Value = "A";
s.Save(field);
var field2 = new ListingItemField();
field2.Value = "B";
s.Save(field2);
var value = new ListingItemValue();
value.ListingItem = item;
value.ListingItemField = field;
item.Values.Add(value);
s.Save(value);
var value2 = new ListingItemValue();
value2.ListingItem = item;
value2.ListingItemField = field2;
item.Values.Add(value2);
s.Save(value2);
var value3 = new ListingItemValue();
value3.ListingItem = item2;
value3.ListingItemField = field;
item2.Values.Add(value3);
s.Save(value3);
t.Commit();
}
}
static void Work()
{
var s = ObjectFactory.GetInstance<ISession>();
IList<Listing> foo;
using (var t = s.BeginTransaction())
{
foo = s.QueryOver<Listing>()
.Left.JoinQueryOver<ListingItem>(x => x.Items)
.Left.JoinQueryOver<ListingItemValue>(x => x.Values)
.Left.JoinQueryOver<ListingItemField>(x => x.ListingItemField)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
t.Commit();
}
try
{
Thread.Sleep(100);
var x1 = foo[0];
Thread.Sleep(100);
var x2 = x1.Items[0];
Thread.Sleep(100);
var x3 = x2.Values[0];
Thread.Sleep(100);
var x4 = x2.Values[0].ListingItemField.Value;
}
catch (Exception) { }
}
}
}
Can you please provide details of your mapping. One method to reduce the number of queries (not to one, but to very few) would be to use the batch-size feature in your mapping. That would populate the proxies on way fewer roundtrips than N+1. But really there should be a solution to fetch all data using futures or similar, so please provide mapping.
This is what I usually do:
First of all, are you familiar with .Future() and .FutureValue()? With those you can send several queries in a single roundtrip. It's only two queries here, so it's not big of a deal, but still...
What I am trying to do is:
Prefetch all ListingItems and their Values and Fields to the first level cache so that they don't trigger Lazy Loading. As you can see I don't use a variable in the first query, because I don't need to store the result. I just need for this query to run and 'prefetch' my entities.
I could avoid the Subquery part, but the Subquery helps me avoiding a cartesian product between Listings - Items - Values.
Since each Value has a single Field, I won't have a problem, in the second query, with a cartesian product.
Then, just get the Listing, along with its Items. The .Value; part with trigger the 'execution' of both queries in a single roundtrip to the database.
The result should be this. As I travel through the object graph, all objects should be already in first level cache and no lazy loading should happen.
.
using (var t = s.BeginTransaction())
{
ListingItem liAlias = null
ListingItemValue livAlias = null;
// 'Preload' all ListingItems with their Values and Fields
s.QueryOver<ListingItem>()
.JoinAlias(li => li.Values, () => livAlias, JoinType.LeftOuterJoin)
.Fetch(_ => livAlias.ListingItemField).Eager
.WithSubquery.WhereProperty(li => li.Id).In(
QueryOver.Of<Listing>()
.Where(l => l.Id == id)
.JoinAlias(l => l.Items, () => liAlias, JoinType.LeftOuterJoin)
.Select(_ => liAlias.Id)
)
.Future();
// Get a single Listing w/ all its Items
var listing = s.QueryOver<Listing>()
.Fetch(l => l.Items).Eager
.Where(l => l.Id == id)
.FutureValue()
.Value;
t.Commit();
}
I have to say here that I haven't tested that, so possibly I am missing something. Secondly, I didn't take in account the composite key you mention. I don't know if that will causes any issues, but I can't see why it should.
Please try it out and let me know.
I'm trying to specify a unique column for an entity, using the Fluent NHibernate Automapper Override. For my test class of CodeType, I'd like to make the Type property unique. The goal would be for a "new CodeType()" being created with the same type field as a currently saved CodeType to be overlaid on top of the current entity.
I have the following CodeType class:
public class CodeType : SecurableEntity
{
public virtual string Type { get; set; }
public virtual string Description { get; set; }
/// <summary>
/// This is a placeholder constructor for NHibernate.
/// A no-argument constructor must be available for NHibernate to create the object.
/// </summary>
public CodeType() { }
}
I have the following CodeTypeMap Class:
public class CodeTypeMap : IAutoMappingOverride<CodeType>
{
public void Override(AutoMapping<CodeType> mapping)
{
//Doesn't work. Need a way to specify a column as unique.
mapping.Map(m => m.Type).Unique();
}
}
The override is applied to the AutoMap, through the following:
public AutoPersistenceModel Generate()
{
var mappings = AutoMap.AssemblyOf<User>(new AutomappingConfiguration());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase<SecurableEntity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.Conventions.Setup(GetConventions());
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
mappings.UseOverridesFromAssemblyOf<UserMap>();
mappings.UseOverridesFromAssemblyOf<CodeMap>();
mappings.UseOverridesFromAssemblyOf<CodeTypeMap>();
return mappings;
}
I'd like the following code to update any existing record with "type" equal to "existingType".
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
CodeType ct = new CodeType();
ct.type = "existingType";
ct = ctr.SaveOrUpdate(ct);
How can I make NHibernate key off of the type field as unique?
Is this possible?
short answer, what you want is something you have to handle in code because there are so many possibilities. Everytime you create a new CodeType you have to check the db if there is already one
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
CodeType ct = ctr.GetByType("existingType");
if (ct == null)
{
ct = new CodeType { type = "existingType" };
}
ctr.SaveOrUpdate(ct);
or
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
CodeType ct = ctr.GetByType("existingType");
if (ct != null)
{
ctr.Detach(ct);
ctr.Merge(new CodeType{ type = "existingType" });
}
or
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
int ctId = ctr.GetIdByType("existingType");
if (ct != 0)
{
ctr.Merge(new CodeType{ Id = ctId, type = "existingType" });
}
and there are some things which can be written differently
public CodeType() { } can be removed or made protected CodeType() { } if not needed for your domain
public AutoPersistenceModel Generate()
{
return AutoMap.AssemblyOf<User>(new AutomappingConfiguration())
.IgnoreBase<Entity>()
.IgnoreBase<SecurableEntity>()
.IgnoreBase(typeof(EntityWithTypedId<>))
.Conventions.Setup(GetConventions())
.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
}
I'm trying to get the AutoPersistence model to map several composite elements. However, it seems that either I end up mapping it as an entity, dropping down to manual mapping or it just doesn't plain work. Here's some code that demonstrates my problem:
using System;
using System.Collections.Generic;
using FluentNHibernate.AutoMap;
using FluentNHibernate.Cfg;
using FluentNHibernate.Conventions.Helpers;
using NHibernate.Cfg;
namespace Scanner {
public class Root {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Component> Components { get; set; }
}
public class Component {
public string Name { get; set; }
}
class Example {
public void DoesntGetComponents()
{
Configuration configuration = new Configuration();
configuration.SetProperty("ConnectionString", "");
configuration.SetProperty("dialect", "NHibernate.Dialect.MsSql2005Dialect");
var config = Fluently.Configure(configuration)
.Mappings(m => m.AutoMappings.Add(AutoMapping))
.BuildConfiguration();
var sql2005 = new NHibernate.Dialect.MsSql2005Dialect();
foreach (var line in config.GenerateSchemaCreationScript(sql2005))
{
Console.WriteLine(line);
}
}
static AutoPersistenceModel AutoMapping() {
AutoPersistenceModel model = new AutoPersistenceModel();
return model
.AddEntityAssembly(typeof(Root).Assembly)
.WithSetup(e => e.IsComponentType = t => t == typeof(Component))
.Where(t => t == typeof(Root))
.MergeWithAutoMapsFromAssemblyOf<Root>()
.ConventionDiscovery.Add(ForeignKey.Format((p, t) => (p == null ? t.Name : p.Name) + "Id"))
.ConventionDiscovery.Add(Table.Is(t => t.EntityType.Name))
;
}
}
}
(Sorry it's so long, but it's the minimal code required to demonstrate the problem. This particular version of the code fails to register the component type at all.
So, what am I doing wrong?
It seems that the component in itself is not the problem, but the mapping of a collection of components. If you would map the component directly onto the Root class, this would not be any problem.
A possible workaround is making the Component class an entity (adding an ID) and overriding the mapping of Components to cascade + auto delete orphans:
AutoPersistenceModel
.ForTypesThatDeriveFrom<Root>(map => map.HasMany(root => root.Components).Cascade.AllDeleteOrphan())