Right now i'm exploring LINQ-to-SQL using Visual Studio 2010 beta. I'm trying to understood the bascis for now, without magics light code auto generated from schemes and from sqlmetal. I have tried the following code:
public class Database : DataContext
{
public Database( string s ) : base( s ) {}
public Table< DUser > items;
}
[Table( Name = "users" )]
public class Item
{
[Column]
public string s;
}
// Using SQL Compact.
var db = new Database( "Data Source=test.sdf;" );
// Works fine and creates database.
db.CreateDatabase();
But how to ADD data to database created / opened? Tutorials i have read shows something like db.Items.Add(), but Table<> don't have Add() member :(. Any insigths without using scheme / sqlmetal?
You're looking for the Table<T>.InsertOnSubmit method (followed of course by DataContext.SubmitChanges). Similarly DeleteOnSubmit for deletions.
Related
I have been through the tutorial on how to create mobile apps with azure and was able to deploy my middle ware and connect to a table called "todoitem" (which was from the tutorial) and I was able to write data to it and read from it.
(https://learn.microsoft.com/en-us/azure/developer/mobile-apps/azure-mobile-apps/quickstarts/maui/?pivots=vs2022-windows)
I was also able to connect with SSMS to the db and see the table and the items there.
Now I wanted to create my own table (useritem).
I copied everything from the tutorial, and tried creating data on my server but was returned: 500 internal server error.
I tried everything, but never was my table created, nor could I write to it.
Then I thought:
I can just create a table in SSMS via console. So I wrote a create table statement, that looked identical to the table form the todo items, only that I also added "email" as a property.
This went through fine and I saw my table in the tree in SSMS.
I could also query data from it (all from SSMS).
When I now tried to write data to my table from my app
await _table.InsertItemAsync(item);
I was finally no longer given "internal server error".
And via SSMS I could see that my data was put on the server!
Yeiks,
No where in the tutorial did it mention that I had to set the tables up myself but anyway, things where working..
UNTIL...
I tried quering data FROM the table FROM my app (not via SSMS, that works fine).
SO I did what I already did in the tutorial which was:
return await _table.GetAsyncItems().ToListAsync();
Which when done with the TODO item s(from the tutorial) returned a list with all items on that table.
BUt when I do it now with my own table, the app crashes, I dont even get an error message, it just doesnt continue.
So my guess it, that somehow, I missed setting up the connection to the table write.
With my workaround of creating the table in SSMS I was able to write to it from my app, but still cannot get my data returned.
What part of the tutorial was I missing?
Here is some of the code in question that should be important:
program.cs:
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("C5"); // set the connection string name that you set up (caused issues before)
if (connectionString == null)
{
throw new ApplicationException("DefaultConnection is not set");
}
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncControllers();
var app = builder.Build();
// Initialize the database
using (var scope = app.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await context.InitializeDatabaseAsync().ConfigureAwait(false);
}
// Configure and run the web service.
app.MapControllers();
app.Run();
appDbcontext.cs:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
/// <summary>
/// The dataset for the UserItems.
/// </summary>
public DbSet<UserItem> UserItems => Set<UserItem>();
/// <summary>
/// Do any database initialization required.
/// </summary>
/// <returns>A task that completes when the database is initialized</returns>
public async Task InitializeDatabaseAsync()
{
await this.Database.EnsureCreatedAsync().ConfigureAwait(false);
}
}
my controller:
[Route("tables/useritem")]
public class tblUserController : TableController<UserItem>
{
public tblUserController(AppDbContext context)
: base(new EntityTableRepository<UserItem>(context))
{
}
}
and ofcourse the modeL.
public class UserItem : EntityTableData
{
[Required, MinLength(1)]
public string Email { get; set; } = "";
public string Telephone { get; set; } = "";
public string Password { get; set; } = "";
}
EDIT:
Also I have noticed that when I look at the link for my middleware, instead of getting a list of the items (which was the case with the todo items) I get this:
Is it possible to change database type at runtime? If yes, how it can be done? I am using EntityFramework 6.
Background about the question:
I have an application that initially does not have database access. A user first has to go through "installation" process and provide information about the database(including database type eg. MySql or MsSql).
I would like to avoid having 2 contexts if possible. If necessary I can provide more details.
You can specify the connection string at runtime using the following...
DbContext has a constructor that can be overloaded with the name of the connection string, or the connection string itself.
public partial class EntityName: DbContext {
public EntityName(): base("name=EntityName") {}
public EntityName(string connectionString): base(connectionString) {}
}
var connString = "PopulateConnString";
Using (var ctx = new EntityName(EntityConnectionStringBuilder)
{
// Do stuff
}
I have the following dependency chain:
IUserAppService
IUserDomainService
IUserRepository
IUserDataContext - UserDataContextImpl(string conn)
All interfaces above and implementations are registered in a Windsor Castle container. When I use one connection string, everything works fine.
Now we want to support multiple databases, In UserAppServiceImpl.cs, we want to get different IUserRepository (different IUserDatabaseContext) according to userId as below:
// UserAppServiceImpl.cs
public UserInfo GetUserInfo(long userId)
{
var connStr = userId % 2 == 0 ? "conn1" : "conn2";
//var repo = container.Resolve<IUserRepository>(....)
}
How can I pass the argument connStr to UserDataContextImpl?
Since the connection string is runtime data in your case, it should not be injected directly into the constructor of your components, as explained here. Since however the connection string is contextual data, it would be awkward to pass it along all public methods in your object graph.
Instead, you should hide it behind an abstraction that allows you to retrieve the proper value for the current request. For instance:
public interface ISqlConnectionFactory
{
SqlConnection Open();
}
An implementation of the ISqlConnectionFactory itself could depend on a dependency that allows retrieving the current user id:
public interface IUserContext
{
int UserId { get; }
}
Such connection factory might therefore look like this:
public class SqlConnectionFactory : ISqlConnectionFactory
{
private readonly IUserContext userContext;
private readonly string con1;
private readonly string con2;
public SqlConnectionFactory(IUserContext userContext,
string con1, string con2) {
...
}
public SqlConnection Open() {
var connStr = userContext.UserId % 2 == 0 ? "conn1" : "conn2";
var con = new SqlConnection(connStr);
con.Open();
return con;
}
}
This leaves us with an IUserContext implementation. Such implementation will depend on the type of application we are building. For ASP.NET it might look like this:
public class AspNetUserContext : IUserContext
{
public string UserId => int.Parse(HttpContext.Current.Session["UserId"]);
}
You have to start from the beginning of your dependency resolver and resolve all of your derived dependencies to a "named" resolution.
Github code link:https://github.com/castleproject/Windsor/blob/master/docs/inline-dependencies.md
Example:
I have my IDataContext for MSSQL and another for MySQL.
This example is in Unity, but I am sure Windsor can do this.
container.RegisterType(Of IDataContextAsync, dbEntities)("db", New InjectionConstructor())
container.RegisterType(Of IUnitOfWorkAsync, UnitOfWork)("UnitOfWork", New InjectionConstructor(New ResolvedParameter(Of IDataContextAsync)("db")))
'Exceptions example
container.RegisterType(Of IRepositoryAsync(Of Exception), Repository(Of Exception))("iExceptionRepository",
New InjectionConstructor(New ResolvedParameter(Of IDataContextAsync)("db"),
New ResolvedParameter(Of IUnitOfWorkAsync)("UnitOfWork")))
sql container
container.RegisterType(Of IDataContextAsync, DataMart)(New HierarchicalLifetimeManager)
container.RegisterType(Of IUnitOfWorkAsync, UnitOfWork)(New HierarchicalLifetimeManager)
'brands
container.RegisterType(Of IRepositoryAsync(Of Brand), Repository(Of Brand))
controller code:
No changes required at the controller level.
results:
I can now have my MSSQL context do its work and MySQL do its work without any developer having to understand my container configuration. The developer simply consumes the correct service and everything is implemented.
Hi I'm trying to create a basic data model / layer
The idea is to have:
Task task = TaskRepository.GetTask(2);
task.Description = "The task has changed";
task.Save();
Is this possible? I've tried the code below
Note: The TaskRepository.GetTask() methods detaches the Task entity.
I'd expect this to work, any ideas why it doesnt?
Thanks
public partial class Task
{
// Place custom code here.
public void Save()
{
using (TinyTaskDataContext db = new TinyTaskDataContext { Log = Console.Out })
{
db.Task.Attach(this);
db.SubmitChanges();
}
}
#region Metadata
// For more information about how to use the metadata class visit:
// http://www.plinqo.com/metadata.ashx
[CodeSmith.Data.Audit.Audit]
internal class Metadata
{
// WARNING: Only attributes inside of this class will be preserved.
public int TaskId { get; set; }
[Required]
public string Name { get; set; }
[Now(EntityState.New)]
[CodeSmith.Data.Audit.NotAudited]
public System.DateTime DateCreated { get; set; }
}
#endregion
}
Having done some reading I've realised I was implmenting the Repository pattern incorrectly. I should have been adding the Save method to the repository for conventions sake.
However, the actually problem I was having with regard to commiting the disconnected dataset was due to optimistic concurrency. The datacontext's job is to keep track of the state of it's entities. When entities become disconnected you loose that state.
I've found you need to add a timestamp field to the database table or I can set the UpdateCheck field on each column in my dbml file.
Here is some info about the UpdateCheck
Some useful links about disconnected Linq and plinqo
Great info on implementing the Repository pattern with LINQ
Short tutorial for implementing for updating and reattaching entities
Previously answer question
Rick Strahl on LINQ to SQL and attaching Entities
There is no need for this line (Task task = new Task();). The above should work although I've never seen it implemented in this manner. Have you thought about using the managers? Are you running into any runtime errors?
Thanks
-Blake Niemyjski
Summary:
I want to save two classes of the same name and different namespaces with the Fluent NHibernate Automapper
Context
I'm writing having to import a lot of different objects to database for testing. I'll eventually write mappers to a proper model.
I've been using code gen and Fluent NHibernate to take these DTOs and dump them straight to db.
the exception does say to (try using auto-import="false")
Code
public class ClassConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.Table(instance.EntityType.Namespace.Replace(".", "_"));
}
}
namespace Sample.Models.Test1
{
public class Test
{
public virtual int Id { get; set; }
public virtual string Something { get; set; }
}
}
namespace Sample.Models.Test2
{
public class Test
{
public virtual int Id { get; set; }
public virtual string SomethingElse { get; set; }
}
}
And here's the actual app code
var model = AutoMap.AssemblyOf<Service1>()
.Where(t => t.Namespace.StartsWith("Sample.Models"))
.Conventions.AddFromAssemblyOf<Service1>();
var cfg = Fluently.Configure()
.Database(
MySQLConfiguration.Standard.ConnectionString(
c => c.Is("database=test;server=localhost;user id=root;Password=;")))
.Mappings(m => m.AutoMappings.Add(model))
.BuildConfiguration();
new SchemaExport(cfg).Execute(false, true, false);
Thanks I really appreciate any help
Update using Fluent Nhibernate RC1
solution from fluent-nhibernate forums by James Gregory
Got around to having a proper look at
this tonight. Basically, it is down to
the AutoImport stuff the exception
mentioned; when NHibernate is given
the first mapping it sees that the
entity is named with the full assembly
qualified name and creates an import
for the short name (being helpful!),
and then when you add the second one
it then complains that this import is
now going to conflict. So the solution
is to turn off the auto importing;
unfortunately, we don't have a way to
do that in the RC... I've just
commited a fix that adds in the
ability to change this in a
convention. So if you get the latest
binaries or source, you should be able
to change your Conventions line in
your attached project to do this:
.Conventions.Setup(x => {
x.AddFromAssemblyOf<Program>();
x.Add(AutoImport.Never()); });
Which adds all the conventions you've
defined in your assembly, then uses
one of the helper conventions to turn
off auto importing.
I was not able to get this to work using Conventions for FluentMappings (in contrast to AutoMappings). However, the following works for me, though it must be added to each ClassMap where needed.
public class AMap : ClassMap<A>
{
public AMap()
{
HibernateMapping.Not.AutoImport();
Map(x => x.Item, "item");
...
}
}
I am having real problem with this, and the example above or any of its variants do not help.
var cfg = new NotifyFluentNhibernateConfiguration();
return Fluently.Configure()
.Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005
.ConnectionString("Server=10.2.65.227\\SOSDBSERVER;Database=NotifyTest;User ID=NHibernateTester;Password=test;Trusted_Connection=False;")
)
.Mappings(m => {
m.AutoMappings
.Add(AutoMap.AssemblyOf<SubscriptionManagerRP>(cfg));
m.FluentMappings.Conventions.Setup(x =>
{
x.AddFromAssemblyOf<Program>();
x.Add(AutoImport.Never());
});
} )
.BuildSessionFactory();
I can't find Program's reference..
I've also tried to put down a seperate xml file to in desperation config fluent nhibernate's mapping to auto-import = false with no success.
Can I please have some more extensive example on how to do this?
Edit, I got the latest trunk just weeks ago.
Edit, Solved this by removing all duplicates.
I have had the same problem. I solved it like this:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(...)
.AdoNetBatchSize(500))
.Mappings(m => m.FluentMappings
.Conventions.Setup(x => x.Add(AutoImport.Never()))
.AddFromAssembly(...)
.AddFromAssembly(...)
.AddFromAssembly(...)
.AddFromAssembly(...))
;
The imported part is: .Conventions.Setup(x => x.Add(AutoImport.Never())). Everything seems to be working fine with this configuration.
Use the BeforeBindMapping event to gain access to the object representation of the .HBM XML files.
This event allows you to modify any properties at runtime before the NHibernate Session Factory is created. This also makes the FluentNHibernate-equivalent convention unnecessary. Unfortunately there is currently no official documentation around this really great feature.
Here's a global solution to duplicate mapping problems ( Just remember that all HQL queries will now need to use Fully Qualified Type names instead of just the class names ).
var configuration = new NHibernate.Cfg.Configuration();
configuration.BeforeBindMapping += (sender, args) => args.Mapping.autoimport = false;
I had to play around with where to add the convention AutoImport.Never() to. I have my persistence mapping separated into different projects - models for each application can also be found in different projects. Using it with Fluent NHibernate and auto mapping.
There are occasions when domains, well mappings really have to be combined. This would be when I need access to all domains. POCO classes used will sometimes have the same name and different namespaces, just as examples above.
Here is how my combine all mapping looks like:
internal static class NHIbernateUtility
{
public static ISessionFactory CreateSessionFactory(string connectionString)
{
return Fluently.Configure()
.Database(
MsSqlConfiguration
.MsSql2008
.ConnectionString(connectionString))
.Mappings(m => m.AutoMappings
.Add(ProjectA.NHibernate.PersistenceMapper.CreatePersistenceModel()))
.Mappings(m => m.AutoMappings
.Add(ProjectB.NHibernate.PersistenceMapper.CreatePersistenceModel()))
.Mappings(m => m.AutoMappings
.Add(ProjectC.NHibernate.PersistenceMapper.CreatePersistenceModel())).BuildSessionFactory();
}
}
And one of the persistence mappers:
public static class PersistenceMapper
{
public static AutoPersistenceModel CreatePersistenceModel()
{
return
AutoMap.AssemblyOf<Credential>(new AutoMapConfiguration())
.IgnoreBase<BaseEntity>()
.Conventions.Add(AutoImport.Never())
.Conventions.Add<TableNameConvention>()
.Conventions.Add<StandardForeignKeyConvention>()
.Conventions.Add<CascadeAllConvention>()
.Conventions.Add<StandardManyToManyTableNameConvention>()
.Conventions.Add<PropertyConvention>();
}
}
Persistence mappers are very similar for each POCO namespace - some have overrides. I had to add .Conventions.Add(AutoImport.Never()) to each persistence mapper and it works like a charm.
Just wanted to share this if anyone else is doing it this way.