I need to set the ConnectionString for my DataContext's based on an AppSetting. I am trying to do this by creating a Partial Class for each DataContext. The below is what I have so far and I am wondering if I am overlooking something?
Specifically, am I dealing with my DataContext's correctly(disposing, staleness, etc)?
Doing it this way will I have issues with Updates and Inserts? Is the file BLLAspnetdb.cs useful or neccessary in the least or should all of that be in the generated partial class AspnetdbDataContext file?
In short, is this an acceptable structure or will this cause me issues as I elaborate it?
dbml File Name = Aspnetdb.dbml
Partial Class File Name = Aspnetdb.cs
partial class AspnetdbDataContext
{
public static bool IsDisconnectedUser
{
get
{
return Convert.ToBoolean(ConfigurationManager.AppSettings["IsDisconnectedUser"]) == true;
}
}
public static AspnetdbDataContext New
{
get
{
var cs = IsDisconnectedUser ? Settings.Default.Central_aspnetdbConnectionString : Settings.Default.aspnetdbConnectionString;
return new AspnetdbDataContext(cs);
}
}
}
My Created File Name = BLLAspnetdb.cs
public class BLLAspnetdb
{
public static IList WorkerList(Guid userID)
{
var DB = AspnetdbDataContext.New;
var workers = from user in DB.tblDemographics
where user.UserID == userID
select new { user.FirstName, user.LastName, user.Phone };
IList theWorkers = workers.ToList();
return theWorkers;
}
public static String NurseName(Guid? userID)
{
var DB = AspnetdbDataContext.New;
var nurseName = from demographic in DB.tblDemographics
where demographic.UserID == userID
select demographic.FirstName +" " + demographic.LastName;
return nurseName.SingleOrDefault();
}
public static String SocialWorkerName(Guid? userID)
{
var DB = AspnetdbDataContext.New;
var swName = from demographic in DB.tblDemographics
where demographic.UserID == userID
select demographic.FirstName + " " + demographic.LastName;
return swName.SingleOrDefault();
}
}
see this previous question and the accepted answer for background on how I got to here...
switch-connectionstrings-between-local-and-remote-with-linq-to-sql
You should dispose of your context, since it is disposable. Consider wrapping the statements in a using block whenever you create a new context.
I propably would express the static "New" property as a "Create" method instead. It is not normal for properties to be creating new objects, so other developers that need to use the code might be surprised by the behavior.
Other than that, your approach will work. When you acquire your context, the logic to determine the connection string runs, and you will get a context constructed with the correct connection string.
I would not all of your methods and properties be static if I were you. It defies good OO design, and makes you very locked in to a specific implementation - however, I guess that is not in scope for the question.
Related
I am new with c# controllers and I am trying to join two entities with a LINQ query. But I am getting a 'missing a cast' error message as shown below that I don't understand and it does not help me to correct the error.
The controller looks like:
public class SoonDueReportsController_simple : BaseODataController
{
public SoonDueReportsController_simple(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public SoonDueReportsController_simple Get()
{
var falligkeiten = UnitOfWork.GetAll<Fälligkeiten>();
var betriebe = UnitOfWork.GetAll<Betriebe>();
var query = (from betrieb in betriebe.AsQueryable()
join fallig in falligkeiten.AsQueryable()
on betrieb.ID equals
fallig.Betrieb_ID
where fallig.Fälligkeit_Erledigt_Datum == null
&& betrieb.Aktiv == true
select new
{
BetriebId = betrieb.ID,
FalligkeitObject = fallig.Fälligkeit_Objekt
});
return query;
}
}
This type of controller I have used with success for single entities (tables from an sql db) to display static data in a kendo grid. But I fail when I try to join two tables as shown above. If someone could help me with this problem I'd appreciate it very much.
Regards, Manu
You select a collection of anonymous objects
select new
{
BetriebId = betrieb.ID,
FalligkeitObject = fallig.Fälligkeit_Objekt
});
And want the method to return a instance of certain type. C# is the strongly typed language without type inference, which means you have to specifically create objects of a certain type or interface if you want to return them.
Also, you are have the controller type itself to be returned from the Get method. This makes no sense. I actually do not know what you want to do but may be this would work:
public class SoonDueReportsController_simple : BaseODataController
{
public SoonDueReportsController_simple(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<SomeModel> Get()
{
var falligkeiten = UnitOfWork.GetAll<Fälligkeiten>();
var betriebe = UnitOfWork.GetAll<Betriebe>();
var query = (from betrieb in betriebe.AsQueryable()
join fallig in falligkeiten.AsQueryable()
on betrieb.ID equals
fallig.Betrieb_ID
where fallig.Fälligkeit_Erledigt_Datum == null
&& betrieb.Aktiv == true
select new SomeModel
{
BetriebId = betrieb.ID,
FalligkeitObject = fallig.Fälligkeit_Objekt
});
return query;
}
}
public class SomeModel
{
public int BetriebId { get; set; }
public string FalligkeitObject { get; set; }
}
Please bear in mind that there are no such things as "C# controllers". You are working with OData, so I would recommend you to look at some OData resources, there are plenty of examples out there.
And one last thing, don't get me wrong, but it does not help giving properties, types and other identifiers German names. People would have hard time trying to understand your code.
The exception explains to you the problem exactly. You're wanting to return a type of 'SoonDueReportsController_simple' and yet you are returning a Queryable where a' is your new { ..., ... } object.
I like the suggestion given to make a strong typed object and fill it but you can also return a dynamic type.
This code works to explain:
private dynamic Get() => new { Name = "SomeName", Age = 31 };
private void TestGet()
{
var obj = Get();
var name = obj.Name;
var age = obj.Age;
}
I use EF6. In my BL-Layer I have following static class, working with my context, which implements DBContext:
public static class AppEnvironment
{
public static IUser CurrentUser { get; set; }
private static IKernel AppKernel { get; set; }
public static void InjectDependencies(params NinjectModule[] contextNinjectModule)
{
AppKernel = new StandardKernel(contextNinjectModule);
}
public static void Authorize(string login, string password)
{
using (var context = AppKernel.Get<ICaseContext>())
{
IUser userToBeAuthorized = context.GetAll<User>().FirstOrDefault(u => u.Login == login);
if (userToBeAuthorized != null && User.GetMD5Hash(password) == userToBeAuthorized.PasswordHash)
{
AppEnvironment.CurrentUser = userToBeAuthorized;
context.Insert(
LogRecord.CreateLogRecord(
userToBeAuthorized,
"Авторизация (успешно)",
LogAction.Read));
}
}
}
public static ICollection<CaseEntityType> GetCaseListTiny<CaseEntityType>(string queryComment) where CaseEntityType : CaseEntityBase
{
using (var context = AppKernel.Get<ICaseContext>())
{
var grantedCaseTypesIDs = AppEnvironment.CurrentUser.CaseTypesGranted.Select(casetype => casetype.ID).ToList();
var cases = context.GetAllIncluding<TaskEntityType>(AppEnvironment.CurrentUser, queryComment, LogAction.Read, t => t.CaseType
).Where(t => grantedTaskTypesIDs.Contains(t.CaseType.ID)).ToList();
return cases;
}
}
}
In my UI Layer I'm trying to use smth like that:
class Program
{
static void Main(string[] args)
{
AppEnvironment.InjectDependencies(new RealContextNinjectModule());
AppEnvironment.Authorize("UserName", "Password");
var caseList = AppEnvironment.GetCaseListTiny<RegularCase>("Get a list");
foreach (var item in caseList)
{
Console.WriteLine(item.Name);
}
}
}
But it throws ObjectContextDisposedException. Can anybody explain what's the right way of dealing with EF6 context class? How should I use it in my BLL or UIL? Why I'm not allowed to use my context class again, after it has been disposed once? I've read a lot of similar questions, but everybody says only smth about eager/lazy loading.
The reason that "everybody says only smth about eager/lazy loading" is because that is what is happening. The sequence of events is:
var caseList = AppEnvironment.GetCaseListTiny<RegularCase>("Get a list");
GetCaseTinyList then calls AppKernel.Get which returns the context into a local var context wrapped up in a using block. Within the block, it created a collection by issuing a call against the context. It does not access that list, and so the collection is not in fact populated; no SQL is run until the collection is accessed. This is lazy eveluation.
At the end of the using block, it disposes of the context. This closes the database connection and marks the context object as disposed and thus unusable.
GetCaseTinyList returns, passing the unpopulated collection back.
Finally, you run a foreach against the returned collection. At the first access the EF collection tries to run the SQL for the GetCaseTinyList against the context. Sadly, the context has been disposed (you did tell it to dispose it and so it did). This makes the system throw the error you are getting, exactly as it should.
One way you can get around this is to change GetCaseTinyCollection to access the collection; something like this:
public static ICollection<CaseEntityType> GetCaseListTiny<CaseEntityType>(string queryComment) where CaseEntityType : CaseEntityBase
{
using (var context = AppKernel.Get<ICaseContext>())
{
var grantedCaseTypesIDs = AppEnvironment.CurrentUser.CaseTypesGranted.Select(casetype => casetype.ID).ToList();
var cases = context.GetAllIncluding<TaskEntityType>(AppEnvironment.CurrentUser, queryComment, LogAction.Read, t => t.CaseType
).Where(t => grantedTaskTypesIDs.Contains(t.CaseType.ID)).ToList();
int count = cases.Count(); // or Count<T>();
return cases;
}
}
This will force the collection to be populated before the context is disposed. However it may have further shortcomings: if the entities in the collection have in turn collection of other objects then when accessed it will try to populate these sub-collections and you're back where you are now.
Another approach is not to use a using/dispose pattern. But at some point you are going to have to clean up the context so you'll have to think about how to do this.
Also, it's far easier when inserting/updating EF entities to keep the context around which you used to read the data. If you don't you have to transfer the updated entities to a new context which sounds like a pain (I've never done it that way).
Basically, you need to rethink your strategy for managing the database context.
I know this question has already been asked but I couldn't find an answer that satisfied me. What I am trying to do is to retrieve a particular DbSet<T> based on its type's name.
I have the following :
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")]
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
public dynamic GetByName_SwitchTest(string name) {
switch (name) {
case "A": return A;
case "B": return B;
}
}
public dynamic GetByName_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return model.GetValue(this);
return null;
}
}
I have no trouble getting the type itself whether it is via a simple switch or reflection. I need however to return the type as a dynamic since I do not know what DbSet type it will be.
Then somewhere else in the same assembly, I use it this way :
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException
At this point model contains an instance of a InternalDbSet<ModelA> type. From there, any use I do with the model object I get a RunTimeBinderException :
'Microsoft.Data.Entity.Internal.InternalDbSet' does not contain a definition for 'FirstOrDefault'
Investigating on the web, I found a blog post explaining that (dixit his blog) :
the reason the call to FirstOrDefault() fails is that the type
information of model is not available at runtime. The reason it's not
available is because anonymous types are not public. When the method
is returning an instance of that anonymous type, it's returning a
System.Object which references an instance of an anonymous type - a
type whose info isn't available to the main program.
And then he points that a solution :
The solution is actually quite simple. All we have to do is open up
AssemplyInfo.cs of the ClassLibrary1 project and add the following
line to it: [assembly:InternalsVisibleTo("assembly-name")]
I did try this solution on my code but it doesn't work. For info I have an asp.net 5 solution with two assemblies running on dnx dotnet46. An app and a dll containing all my models and DbContext. All the concerned calls I do are located on the dll though.
Does this solution have any chance to work ?
Am I missing something ?
Any pointers would be greatly appreciated ?
Thanks in advance
[EDIT]
I have tried to return IQueryable<dynamic> rather than dynamic and I could do the basic query model.FirstOrDefault(); but above all I'd like to be able to filter on a field too :
var record = model.FirstOrDefault(item => item.MyProperty == true);
So how did I do it when I am not aware of <T> during compile time.
First need to get the type as DbContext.Set method returns a non-generic DbSet instance for access to entities of the given type in the context and the underlying store.
public virtual DbSet Set(Type entityType)
Note here argument is the type of entity for which a set should be returned.And set for the given entity type is the return value.
var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);
now once I have this type
if(type != null)
{
DbSet context = context.Set(type);
}
Or a one liner would be
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));
*Disclaimer: This response doesn't give a stricto sensu answer to my question. It is rather a different approach to resolve my own problem. I am aware this is a specific example for a given situation that will not work for everyone. I am posting this approach in the hope it helps someone but will not mark it as the answer as I am still hoping for a real solution.
To start with, let's accept the fact that the only useful information we can get out of the current code is whether a record exists or not.. Any attempt of a dynamic queries after that would give the RuntimeBinderException.
Then let's continue with another fact; DbContext.Add(object) and DbContext.Update(object) are not template based so we can use them to save our models ( Instead of db.A.Add() or db.A.Update() )
In my own situation, no more is required to work out a procedure
Define models a little differently
To start with, I need a field that is retrievable across all my models which should obviously be a way to identify a unique record.
// IModel give me a reliable common field to all my models ( Fits my DB design maybe not yours though )
interface IModel { Guid Id { get; set; } }
// ModelA inherit IModel so that I always have access to an 'Id'
class ModelA : IModel {
public Guid Id { get; set; }
public int OtherField { get; set; }
}
// ModelB inherit IModel so that I always have access to an 'Id'
class ModelB : IModel {
public Guid Id { get; set; }
public string WhateverOtherField { get; set; }
}
Re-purpose the dynamic queries a bit to do something we know works
I haven't found a way to do smart query dynamically, so instead I know I can reliably identify a record and know if it exists or not.
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
// In my case, this method help me to know the next action I need to do
// The switch/case option is not pretty but might have better performance
// than Reflection. Anyhow, this is one's choice.
public bool HasRecord_SwitchTest(string name) {
switch (name) {
case "A": return A.AsNoTracking().Any(o => o.Id == id);
case "B": return B.AsNoTracking().Any(o => o.Id == id);
}
return false;
}
// In my case, this method help me to know the next action I need to do
public bool HasRecord_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return (bool)model.GetValue(this).AsNoTracking().Any(o => o.Id == id);
return false;
}
// Update and save immediately - simplified for example
public async Task<bool> UpdateDynamic(object content)
{
EntityEntry entry = Update(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
// Insert and save immediately - simplified for example
public async Task<bool> InsertDynamic(object content)
{
EntityEntry entry = Add(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
}
A little bit of plumbing to give a sense to my situation
Next, what I needed to do with that dynamic queries was a way to replicate data from a server down to my client. ( I have omitted a big chunk of the architecture to simplify this example )
class ReplicationItem
{
public ReplicationAction Action { get; set; } // = Create, Update, Delete
public string ModelName { get; set; } // Model name
public Guid Id { get; set; } // Unique identified across whole platform
}
Connecting the bits.
Now, here's the routine that connects the bits
public async void ProcessReplicationItem(ReplicationItem replicationItem)
{
using (var db = new MyDbContext())
{
// Custom method that attempts to get remote value by Model Name and Id
// This is where I get the strongly typed object
var remoteRecord = await TryGetAsync(replicationItem.ModelName, replicationItem.Id);
bool hasRemoteRecord = remoteRecord.Content != null;
// Get to know if a local copy of this record exists.
bool hasLocalRecord = db.HasRecord_ReflectionTest(replicationItem.ModelName, replicationItem.Id);
// Ensure response is valid whether it is a successful get or error is meaningful ( ie. NotFound )
if (remoteRecord.Success || remoteRecord.ResponseCode == System.Net.HttpStatusCode.NotFound)
{
switch (replicationItem.Action)
{
case ReplicationAction.Create:
{
if (hasRemoteRecord)
{
if (hasLocalRecord)
await db.UpdateDynamic(remoteRecord.Content);
else
await db.InsertDynamic(remoteRecord.Content);
}
// else - Do nothing
break;
}
case ReplicationAction.Update:
[etc...]
}
}
}
}
// Get record from server and with 'response.Content.ReadAsAsync' type it
// already to the appropriately
public static async Task<Response> TryGetAsync(ReplicationItem item)
{
if (string.IsNullOrWhiteSpace(item.ModelName))
{
throw new ArgumentException("Missing a model name", nameof(item));
}
if (item.Id == Guid.Empty)
{
throw new ArgumentException("Missing a primary key", nameof(item));
}
// This black box, just extrapolate a uri based on model name and id
// typically "api/ModelA/{the-guid}"
string uri = GetPathFromMessage(item);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12345");
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return new Response()
{
Content = await response.Content.ReadAsAsync(Type.GetType(item.ModelName)),
Success = true,
ResponseCode = response.StatusCode
};
}
else
{
return new Response()
{
Success = false,
ResponseCode = response.StatusCode
};
}
}
}
public class Response
{
public object Content { get; set; }
public bool Success { get; set; }
public HttpStatusCode ResponseCode { get; set; }
}
ps: I am still interested in a real answer, so please keep posting for other answer if you have a real one to share.
You could use this to get the DBSet for a specific type:
public object GetByType(DbContextcontext, Type type) {
var methode = _context.GetType().GetMethod("Set", types: Type.EmptyTypes);
if (methode == null) {
return null;
}
return methode.MakeGenericMethod(type).Invoke(_context, null);
}
I want to embed my entity datamodel to my class library that contains methods of database operations. (like MyEntityDatamodel.dll) Simply when i add my entity framework embedded class library to my windows forms application, i am able to use it's methods (insert update delete).
Currently i am able to running methods from dll but the problem is i must add EntityFramework.dll and EntityFramework.SqlServer.dll to my forms application as reference. Otherwise it doesn't work. I want to add only MyEntityDatamodel.dll.
Sample code of my entity datamodel class:
public class MyClass
{
public string classParameter { get; set; }
public void InsertMethod(MyClass parameter)
{
var dbContext = new MyEntities();
InsertOperations...
}
public void UpdateMethod(MyClass parameter)
{
var dbContext = new MyEntities();
UpdateOperations...
}
}
How I am using;
using MyClass;
MyClass myClass = new MyClass();
myClass.classParameter = "example";
myClass.InsertMethod(myClass);
Thanks in advance.
Two things to note...
First, directly to your question - you can't. You may try some sophisticated techniques like dynamically loading the assemblies your program needs, but this changes almost nothing except that you do not have them listed in your project references. They still need to be somewhere where the loader can find them. And more important they must always be deployed together with your app. Otherwise it won't work.
Second, DbContext is meant to be used as a Unit-of-Work design pattern, that is it needs to be disposed after the unit has finished whatever it was meant to do. Usualy, the using clause is a very good choice for this, so consider wrapping your code this way:
using (var dbContext = new MyEntities())
{
InsertOperations.../UpdateOperations...
}
After spending hours of on this, finally i found a solution. I am able to inject connection string to my dbcontext before my dbcontext object was created.
First, i changed my dbcontext's constructor method. I added string parameter for connection string instead reading from the web.config.
I changed this;
public partial class ExampleEntities : DbContext
{
public ExampleEntities()
: base("name=ExampleEntities")
{
}
}
To this;
public partial class ExampleEntities : DbContext
{
public ExampleEntities(string connStr) : base(connStr)
{
}
}
Then i added a method for Generating connection string to my code;
public static string CreateConnStr(string dataSource, string instanceName, string userName, string password)
{
string connectionString = new System.Data.Entity.Core.EntityClient.EntityConnectionStringBuilder
{
Metadata = "res://*/ExampleModel.csdl|res://*/ExampleModel.ssdl|res://*/ExampleModel.msl",
Provider = "System.Data.SqlClient",
ProviderConnectionString = new System.Data.SqlClient.SqlConnectionStringBuilder
{
InitialCatalog = instanceName,
DataSource = dataSource,
IntegratedSecurity = false,
UserID = userName,
Password = password,
}.ConnectionString
}.ConnectionString;
return connectionString;
}
Now i am simply use my dbcontext shown as below;
var dbContext = new ExampleEntities(CreateConnStr("localhost\\SQLEXPRESS","ExampleDataBase","UserName", "Password"));
Hope it helps.
P.s.: Sorry for bad grammar. :)
I have this working in my controller, but I want to follow best practices and put my database logic in Model.
I want to put all database logic (select, update, delete, insert) to MODEL, therefore I create methods in my model.
My method for retrieving the data:
public IQueryable<ChatLogsNameViewModel> getChatLogWithName()
{
using (var db = new ChatLogContext())
{
var list = (from b in db.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list;
}
}
This is my modelView:
public class ChatLogsNameViewModel
{
public UserProfile UserProfile { get; set; }
public string Message { get; set; }
public DateTime Time { get; set; }
}
I call my getChatLogWithName() method in my controller like this:
List<ChatLogsNameViewModel> items = null;
using (var dba = new ChatLogContext())
{
items = dba.getChatLogWithName().ToList();
return View(items);
}
Error I get is:
The operation cannot be completed because the DbContext has been disposed.
What is the proper way to do this? I just want to pass collection (all records from 2 tables via join) to controller.
To ensure that the DBContext is not getting referenced after disposal. How about returning a list so you dont have to call .ToList():
public List<ChatLogsNameViewModel> getChatLogWithName()
{
using (var db = new ChatLogContext())
{
var list = (from b in db.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list.ToList();
}
}
and
items = dba.getChatLogWithName();
Or
Since it appears that dba is the same as db, couldn't you change your code to use the dba instance which won't get disposed until the end of your using statement within your controller.
public IQueryable<ChatLogsNameViewModel> getChatLogWithName()
{
var list = (from b in this.ChatLogs
select new ChatLogsNameViewModel()
{
UserProfile = b.UserProfile,
Message = b.Message,
Time = b.Time
});
return list;
}
Lifetime - DbContext
The lifetime of the context begins when the instance is created and
ends when the instance is either disposed or garbage-collected. Use
using if you want all the resources that the context controls to be
disposed at the end of the block. When you use using, the compiler
automatically creates a try/finally block and calls dispose in the
finally block.
The problem was when the inner using got disposed, it invalidated the DbContext. So you need to use .ToList() to save the query result in memory.
Suppose getChatLogWithName is defined in the class called Repo, you can change the controller logic to something like this:
var repo = new Repo();
var items = repo.getChatLogWithName().ToList();
Or move .ToList() to getChatLogWithName.
Btw, you should not use the nested DbContexts cope, in your controller, you don't have to wrap it using another DbContextscope.