I have a Web API where my repository class that has data hard-coded. I want to modify it to grab the same data from SQL Server database. I created the DB Context class in the DAL folder and a connection string in my web.config file with the same name as context class - MyClassesContext
public class myRepository
{
public myClasses.Type[] GetAllTypes()
{
return new myClasses.Type[]
{
new myClasses.Type
{
typeId="1",
typeVal = "New"
},
new myClasses.Type
{
typeId="2",
typeVal = "Old"
}
};
}
public myClasses.Employee[] GetAllEmployees()
{
return new myClasses.Employee[]
{
new myClasses.Employee
{
empId="111111",
empFName = "Jane",
empLName="Doe"
},
new myClasses.Employee
{
empId="222222",
empFName = "John",
empLName="Doe"
}
};
}
public bool VerifyEmployeeId(string id)
{
myClasses.Employee[] emp = new myClasses.Employee[]
{
new myClasses.Employee
{
empId="111111",
empFName = "Jane",
empLName="Doe"
},
new myClasses.Employee
{
empId="222222",
empFName = "John",
empLName="Doe"
}
};
for (var i = 0; i <= emp.Length - 1; i++)
{
if (emp[i].empId == id)
return true;
}
return false;
}
}
and my model class:
public class myClasses
{
public class Employee
{
public string empId { get; set; }
public string empFName { get; set; }
public string empLName { get; set; }
}
public class Type
{
public string typeId { get; set; }
public string typeVal { get; set; }
}
}
and here is my DBContext:
using System.Data.Entity;
using myClassesAPI.Models;
namespace myClassesAPI.DAL
{
public class myClassesContext : DbContext
{
public DbSet<myClasses.Employee> Employees { get; set; }
public DbSet<myClasses.Type> Types { get; set; }
}
}
Now the missing link here is how do I connect DBContext class. I did a great amount of googling but was not able to find anything relevant. Was wondering if anyone could point me in the right direction
Taking into consideration that everything is configured and working (Connection String, database access etc), in your repository, just make use of your DbContext class. Example to get a list of all Types:
public List<Type> GetAllTypes()
{
//Create an instance of your dbContext Class.
using (myClassesContext context = new myClassesContext ())
{
//Return all the Type's
return context.Type.ToList()
}
}
I think you need to read and study more about EntityFramework to get a good understanding on how things work, how you can query your database using LINQ. Here's a great starting point:
http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
EDIT
I use this example of a connection string:
<add name="myClassesContext "
providerName="System.Data.SqlClient"
connectionString="Data Source=ServerName\InstanceName;Initial Catalog=DatabaseName;Integrated Security=True;MultipleActiveResultSets=True" />
Then, you set the same connection string name in your DbContext file:
public myClassesContext ()
: base(Connection.GetConnectionString("myClassesContext"))
{
}
How about using a connection string to the actual DB after specifying the database connection string to which you want to connect to:
public class myClassesContext : DbContext
{
public myClassesContext ()
: base("connection_string")
{
}
}
<configuration>
<connectionStrings>
<add name="connection_string"
providerName="..."
connectionString="..."/>
</connectionStrings>
</configuration>
Related
I'm working on an asp.net core project that has both sql server and mongodb. the task is to add or update any entity that is being added or updated to sql server(using ef core), to mongo.
I figured I can override OnSaveChangesAsync but I have no idea how to access the entities that are being changed there in order to call the mongo service's update and insert methods on them.
This can be done by using interceptors:
The Model:
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
DbContext:
public class StudentsContext : DbContext
{
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.AddInterceptors(new UpdateMongoInterceptor())
.UseSqlite(#"DataSource=C:\Databases\Students.db");
}
}
SaveChangesInterceptor:
public class UpdateMongoInterceptor : SaveChangesInterceptor
{
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
UpdateMongoDb(eventData.Context);
return result;
}
private static void UpdateMongoDb(DbContext context)
{
context.ChangeTracker.DetectChanges();
foreach (var entry in context.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Added:
if (entry.Entity is Student student)
{
//Update MongoDB...
Console.WriteLine($"Push student entity to mongoDB Id: {student.Id}, Name:{student.Name}, Age:{student.Age}");
}
break;
}
}
}
Usege:
using (var context = new StudentsContext())
{
var student = new Student {Id = Guid.NewGuid(), Name = "Jon Snow", Age = 25};
context.Add(student);
context.SaveChanges();
}
Interceptors documentations can be found here: Example: SaveChanges interception for auditing
** If you are using DB generated keys you should override SavedChanges and figure out of the entity was updated or added.
I want to implement something similar to lazy loading, but don't understand how to implement that. I want to force entity framework core include navigation property for all queries for type which implements my interface
public interface IMustHaveOrganisation
{
Guid OrganisationId { get; set; }
Organisation Organisation { get; set; }
}
public class MyEntity : IMustHaveOrganisation {
public Guid OrganisationId { get; set; }
public virtual Organisation Organisation { get; set; }
}
Without lazy loading I need to add .Include(x=>x.Organisation) to each query literally , and I can't use implementation of lazy loading provided by Microsoft. I need kind of custom implementation of that with loading just one property.
Or even force DbContext somehow to Include that property, it also fine for me.
How can I achieve that?
You can make this work by rewriting the expression tree, before it gets translated by EF Core.
To make this work in a way, where you don't have to specify anything additional in the query, you can hook into the very beginning of the query pipeline and inject the Include() call as needed.
This can be done, by specifying a custom IQueryTranslationPreprocessorFactory implementation.
The following fully working console project demonstrates this approach:
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class Organisation
{
public int OrganisationId { get; set; }
public string Name { get; set; }
}
public interface IMustHaveOrganisation
{
int OrganisationId { get; set; }
Organisation Organisation { get; set; }
}
public class MyEntity : IMustHaveOrganisation
{
public int MyEntityId { get; set; }
public string Name { get; set; }
public int OrganisationId { get; set; }
public virtual Organisation Organisation { get; set; }
}
public class CustomQueryTranslationPreprocessorFactory : IQueryTranslationPreprocessorFactory
{
private readonly QueryTranslationPreprocessorDependencies _dependencies;
private readonly RelationalQueryTranslationPreprocessorDependencies _relationalDependencies;
public CustomQueryTranslationPreprocessorFactory(
QueryTranslationPreprocessorDependencies dependencies,
RelationalQueryTranslationPreprocessorDependencies relationalDependencies)
{
_dependencies = dependencies;
_relationalDependencies = relationalDependencies;
}
public virtual QueryTranslationPreprocessor Create(QueryCompilationContext queryCompilationContext)
=> new CustomQueryTranslationPreprocessor(_dependencies, _relationalDependencies, queryCompilationContext);
}
public class CustomQueryTranslationPreprocessor : RelationalQueryTranslationPreprocessor
{
public CustomQueryTranslationPreprocessor(
QueryTranslationPreprocessorDependencies dependencies,
RelationalQueryTranslationPreprocessorDependencies relationalDependencies,
QueryCompilationContext queryCompilationContext)
: base(dependencies, relationalDependencies, queryCompilationContext)
{
}
public override Expression Process(Expression query)
{
query = new DependenciesIncludingExpressionVisitor().Visit(query);
return base.Process(query);
}
}
public class DependenciesIncludingExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitConstant(ConstantExpression node)
{
// Call Include("Organisation"), if SomeEntity in a
// DbSet<SomeEntity> implements IMustHaveOrganisation.
if (node.Type.IsGenericType &&
node.Type.GetGenericTypeDefinition() == typeof(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<>) &&
node.Type.GenericTypeArguments.Length == 1 &&
typeof(IMustHaveOrganisation).IsAssignableFrom(node.Type.GenericTypeArguments[0]))
{
return Expression.Call(
typeof(EntityFrameworkQueryableExtensions),
nameof(EntityFrameworkQueryableExtensions.Include),
new[] {node.Type.GenericTypeArguments[0]},
base.VisitConstant(node),
Expression.Constant(nameof(IMustHaveOrganisation.Organisation)));
}
return base.VisitConstant(node);
}
}
public class Context : DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<Organisation> Organisations { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// Register the custom IQueryTranslationPreprocessorFactory implementation.
// Since this is a console program, we need to create our own
// ServiceCollection for this.
// In an ASP.NET Core application, the AddSingleton call can just be added to
// the general service configuration method.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkSqlServer()
.AddSingleton<IQueryTranslationPreprocessorFactory, CustomQueryTranslationPreprocessorFactory>()
.AddScoped(
s => LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.BuildServiceProvider();
optionsBuilder
.UseInternalServiceProvider(serviceProvider) // <-- use our ServiceProvider
.UseSqlServer(#"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=62849896")
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>(
entity =>
{
entity.HasData(
new MyEntity {MyEntityId = 1, Name = "First Entity", OrganisationId = 1 },
new MyEntity {MyEntityId = 2, Name = "Second Entity", OrganisationId = 1 },
new MyEntity {MyEntityId = 3, Name = "Third Entity", OrganisationId = 2 });
});
modelBuilder.Entity<Organisation>(
entity =>
{
entity.HasData(
new Organisation {OrganisationId = 1, Name = "First Organisation"},
new Organisation {OrganisationId = 2, Name = "Second Organisation"});
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var myEntitiesWithOrganisations = context.MyEntities
.OrderBy(i => i.MyEntityId)
.ToList();
Debug.Assert(myEntitiesWithOrganisations.Count == 3);
Debug.Assert(myEntitiesWithOrganisations[0].Name == "First Entity");
Debug.Assert(myEntitiesWithOrganisations[0].Organisation.Name == "First Organisation");
}
}
}
Even though no explicit Include() is being made in the query in Main(), the following SQL is being generated, that does join and retrieve the Organisation entities:
SELECT [m].[MyEntityId], [m].[Name], [m].[OrganisationId], [o].[OrganisationId], [o].[Name]
FROM [MyEntities] AS [m]
INNER JOIN [Organisations] AS [o] ON [m].[OrganisationId] = [o].[OrganisationId]
ORDER BY [m].[MyEntityId]
I am developing a .Net project. I am using entity framework code first approach to interact with database. I am seeding some mock data to my database during development. But seeding is not working. I followed this link - http://www.entityframeworktutorial.net/code-first/seed-database-in-code-first.aspx.
This is my ContextInitializer class
public class ContextInitializer : System.Data.Entity.CreateDatabaseIfNotExists<StoreContext>
{
protected override void Seed(StoreContext context)
{
IList<Brand> brands = new List<Brand>();
brands.Add(new Brand { Name = "Giordano" ,TotalSale = 1 });
brands.Add(new Brand { Name = "Nike" , TotalSale = 3 });
foreach(Brand brand in brands)
{
context.Brands.Add(brand);
}
base.Seed(context);
context.SaveChanges();
}
}
This is my context class
public class StoreContext : DbContext,IDisposable
{
public StoreContext():base("DefaultConnection")
{
Database.SetInitializer(new ContextInitializer());
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Brand> Brands { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
This is my brand class
public class Brand
{
public int Id { get; set; }
[Required]
[MaxLength(40)]
public string Name { get; set; }
public int TotalSale { get; set; }
}
I searched solutions online and I followed instructions. I run context.SaveChanges as well. But it is not seeding data to database. Why it is not working?
You are taking the wrong initializer, CreateDatabaseIfNotExists is called only if the database not exists!
You can use for example DropCreateDatabaseIfModelChanges:
Solution 1)
public class ContextInitializer : System.Data.Entity.DropCreateDatabaseIfModelChanges<StoreContext>
{
You have to take care with this approach, it !!!removes!!! all existing data.
Solution 2)
Create a custom DbMigrationsConfiguration:
public class Configuration : DbMigrationsConfiguration<StoreContext>
{
public Configuration()
{
// Take here! read about this property!
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = false;
}
protected override void Seed(StoreContext context)
{
IList<Brand> brands = new List<Brand>();
brands.Add(new Brand { Name = "Giordano", TotalSale = 1 });
brands.Add(new Brand { Name = "Nike", TotalSale = 3 });
foreach (Brand brand in brands)
{
context.Brands.AddOrUpdate(m => m.Name, brand);
}
base.Seed(context);
context.SaveChanges();
}
}
In this way you can called( !!Before you create the DbContext or in the DbContext constructor!!):
// You can put me also in DbContext constuctor
Database.SetInitializer(new MigrateDatabaseToLatestVersion<StoreContext , Yournamespace.Migrations.Configuration>("DefaultConnection"));
Notes:
DbMigrationsConfiguration need to know about the connection string you can provide this info in the constructor or from outside.
In Your DbMigrationsConfiguration you can configure also:
MigrationsNamespace
MigrationsAssembly
MigrationsDirectory
TargetDatabase
If you leave everything default as in my example then you do not have to change anything!
Setting the Initializer for a Database has to happen BEFORE the context is ever created so...
public StoreContext():base("DefaultConnection")
{
Database.SetInitializer(new ContextInitializer());
}
is much to late. If you made it static, then it could work:
static StoreContext()
{
Database.SetInitializer(new ContextInitializer());
}
Your code is working if you delete your existing database and the EF will create and seeding the data
Or
You can use DbMigrationsConfiguration insted of CreateDatabaseIfNotExists and change your code as follow:
First you have to delete the existing database
ContextInitializer class
public class ContextInitializer : System.Data.Entity.Migrations.DbMigrationsConfiguration<StoreContext>
{
public ContextInitializer()
{
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = true;
}
protected override void Seed(StoreContext context)
{
IList<Brand> brands = new List<Brand>();
brands.Add(new Brand { Name = "Giordano", TotalSale = 1 });
brands.Add(new Brand { Name = "Nike", TotalSale = 3 });
foreach (Brand brand in brands)
{
context.Brands.AddOrUpdate(m => m.Name, brand);
}
base.Seed(context);
context.SaveChanges();
}
}
StoreContext
public class StoreContext : DbContext, IDisposable
{
public StoreContext() : base("DefaultConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<StoreContext, ContextInitializer>());
// Database.SetInitializer(new ContextInitializer());
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Brand> Brands { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Then any change in your seed will automatically reflected to your database
Coming from This Example
I have a data context
public class AggregateContext : DbContext
{
public DbSet<BlogEntry> BlogEntries { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
}
And in app start I have this
Database.SetInitializer(new TestingDbInitializer());
new AggregateContext().UserProfiles.Find(1);
And my Initializer looks like this
public class TestingDbInitializer : DropCreateDatabaseAlways<AggregateContext>
{
protected override void Seed(AggregateContext context)
{
AccountsContext(context);
// add a bunch of Lorems to the blog. does call context.SaveChanges();
BlogsContext(context);
}
void AccountsContext(AggregateContext context)
{
WebSecurity.InitializeDatabaseConnection(
"DefaultConnection",
"UserProfile",
"UserId",
"UserName",
autoCreateTables: true);
//create Admin
if (!WebSecurity.ConfirmAccount("Admin"))
{
var confirm = WebSecurity.CreateUserAndAccount(
"Admin",
"password",
new { Email = "please#help.me" });
if (!Roles.RoleExists("Admin"))
Roles.CreateRole("Admin");
Roles.AddUserToRole("Admin", "Admin");
}
}
When I run it I crash on this line.
var confirm = WebSecurity.CreateUserAndAccount(
"Admin",
"password",
new { Email = "please#help.me" });
with the sqlexception "Invalid column name 'Email'."
Looking at my database in server explorer I see that the email column was not created.
public class AggregateContext : DbContext
{
public AggregateContext()
: base("DefaultConnection")
{
}
public DbSet<BlogEntry> BlogEntries { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
}
Forgot to define the connection. Go Me !
Getting the following error:
Schema specified is not valid. Errors:
The types in the assembly 'x, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' cannot be loaded because the assembly contains
the EdmSchemaAttribute, and the closure of types is being loaded by
name. Loading by both name and attribute is not allowed.
What does this error mean exactly?
I'm trying to shoe-horn into my application an EF model from an existing database.
Before this application was based on CodeFirst and using the repository pattern but for the life of me I can't get this working.
Before I had:
public class BaseModelContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
}
But in a EF model-first scenario (one where tables already exist in the db), I had to remove these as it didn't seem to like having a repository pattern on DbSet properties.
So I stripped these out, and the repository can then use repository on the classes already defined on the .designer.cs context class (the EF model). This has the EdmSchemaAttribute set inside the generated code.
So how do I get my repository pattern to work in the model-first scenario? What does the above error mean exactly?
EDIT
Added new code:
public class BaseModelContext : DbContext
{
// public DbSet<Location> Locations { get; set; }
public BaseModelContext(string nameOrConnection)
: base(nameOrConnection)
{
}
public BaseModelContext()
{
}
}
public class VisitoriDataContext : BaseModelContext
{
public VisitoriDataContext()
: base("visitoriDataConnection")
{
}
}
public interface IVisitoriDataContextProvider
{
VisitoriDataContext DataContext { get; }
}
public class VisitoriDataContextProvider : IVisitoriDataContextProvider
{
public VisitoriDataContext DataContext { get; private set; }
public VisitoriDataContextProvider()
{
DataContext = new VisitoriDataContext();
}
}
public class VisitoriRepository<T> : IRepository<T> where T : class
{
protected readonly IVisitoriDataContextProvider _ctx;
public VisitoriRepository(IVisitoriDataContextProvider ctx)
{
_ctx = ctx;
}
public T Get(int id)
{
return _ctx.DataContext.Set<T>().Find(id);
}
}
public interface ILocationRepo : IRepository<Location>
{
IEnumerable<Location> GetSuggestedLocationsByPrefix(string searchPrefix);
}
public class LocationRepo : VisitoriRepository<Location>, ILocationRepo
{
public LocationRepo(IVisitoriDataContextProvider ctx)
: base(ctx)
{
}
public IEnumerable<Location> GetSuggestedLocationsByPrefix(string searchPrefix)
{
return Where(l => l.name.Contains(searchPrefix)).ToList();
}
}
The error means that you cannot combine code first mapping (data annotations and fluent API) and EDMX mapping (with EntityObjects!) for entity with the same name. These two approaches are disjunctive.
The rest of your question is not clear.
Btw. building mapping from existing database is called database first not model first.
Decorate the assembly containing the GILayerModel type with [assembly: EdmSchema] attribute.
In my case, I had a class that derived from an entity (code-first class) in another assembly, and I was adding an instance of this class to the DBContext:
in DBEntities project:
public class GISLayer
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int GISLayerId { get; set; }
[StringLength(200)]
public string LayerName { get; set; }
public List<GISNode> Nodes { get; set; }
}
in the second assembly:
public class GISLayerModel : DBEntities.GISLayer
{
public new List<GISNodeModel> NodesModel { get; set; }
}
and the cause of error:
[WebMethod]
public void SaveGISLayers(GISLayerModel[] layers)
{
using (DBEntities.DBEntities db = new DBEntities.DBEntities())
{
foreach (var l in layers)
{
if (l.GISLayerId > 0)
{
db.GISLayers.Attach(l); //attaching a derived class
db.Entry(l).State = System.Data.EntityState.Modified;
}
else
db.GISLayers.Add(l); //adding a derived class
SaveGISNodes(l.NodesModel.ToArray(), db);
}
db.SaveChanges();
}
}
So, I used AutoMapper to copy properties of derived class to a new instance of base class:
DBEntities.GISLayer gl = AutoMapper.Mapper.Map<DBEntities.GISLayer>(l);
if (gl.GISLayerId > 0)
{
db.GISLayers.Attach(gl);
db.Entry(gl).State = System.Data.EntityState.Modified;
}
else
db.GISLayers.Add(gl);
That solved the problem.