Availability of Methods on the basis of Constructor
I have a Class called Repository for accessing (Read/Write to and from) Database.
The projects that require access to the database create an object of Repository and pass the connection string as a constructor parameter for the repository to work for that particular project.
I have few methods in the Repository that i want to be available only if certain connection strings are passed. I don't want them to be available if some different connection string is passed.
Is there any way I can accomplish that?
I have never used method header technique, can that help? if yes, how can i use it? if no, 开发者_JS百科please let me know if there is any other way to achieve my goal.
Regards.
You could use a factory method pattern to accomplish your goal.
Create a RepositoryFactory class that takes in the connection string and returns a different repository based upon the contents of the string.
public class RepositoryFactory {
public IRepository GetRepository(string connection)
{
if(SomeTestOnConnect(connection))
return new SimpleRepository(connection);
else
return new FullRepository(connection);
}
}
With this when someone wants a repository they call the GetRepository method.
Sort of doing:
if (_connection == "XYZ")
throw new InvalidOperationException();
Is it possible that you could refactor your approach to create a new class:
public class ConnectionInfo
{
public string ConnectionString { get; set; }
public bool IsLimitedAccess { get; set; }
}
Then, in each repository method not allowed, if limited access, return null or throw exception or something?
If you know in advance whether you'll need the extra methods, you could have a base version which doesn't support the extra methods, and a derived type which does. The constructor for the derived type could throw an exception if required information is not provided.
Using a factory method instead of a constructor would allow a base or fancy object to be constructed based upon the passed-in connection string; if you just have one factory method, though, you'll have to typecast the result if you want to use the extra methods.
The best approach may be to have a factory method for each defined type, with a guarantee that it will either return an object that's at least as good as the requested type or throw an exception if it can't. This approach would allow for future expansion in case more further-derived types become available.
If you want to limit the available methods, you could use a pattern like this.
Use the factory to get an instance like this:
var repo = RepositoryFactory.Resovle<IFullRepository>("connection string");
And the code that makes this work is here
public class RepositoryFactory
{
public static T Resovle<T>(string connection) where T: IRepository
{
IRepository instance = new Repository(connection);
return (T)instance;
}
private class Repository : IFullRepository
{
private string _connection;
public Repository(string connection)
{
_connection = connection;
}
public object Get(int id)
{
// select
}
public void Save(object o)
{
// upate
}
public void Create(object o)
{
// create
}
public void CustomMethodOne()
{
// do something specialized
}
public void CustomMethodTwo()
{
// do something specialized
}
}
}
public interface IRepository
{
object Get(int id);
void Save(object o);
void Create(object o);
}
public interface IRepositoryProjectOne: IRepository
{
void CustomMethodOne();
}
public interface IRepositoryProjectTwo: IRepository
{
void CustomMethodTwo();
}
public interface IFullRepository: IRepositoryProjectOne, IRepositoryProjectTwo
{
}
The downsides are, you get an explosion of Interfaces to control what methods are exposed. However, it is possible to cast between the various interfaces, but it avoids having to throw exceptions when a method isn't implemented.
There doesn't seem to be a perfect way to do this however I think your calling method needs to know whether or not it is allowed to write to the Repository as another poster has stated the availability of methods is something that needs to be known at compile time rather than run time.
The solution would be to create two interfaces, one that offers full functionality and one that offers limited functionality.
public interface IRepository : IRead
{
void Write(object o);
}
public interface IRead
{
object Read();
}
Your Repository object then implements the top interface.
public class Repository : IRepository
{
private readonly string _connectionString;
public Repository(string connectionString)
{
_connectionString = connectionString;
}
public object Read()
{
// Do stuff
}
public void Write(object o)
{
// Do stuff
}
}
You can then have a class that determines if the connection string demands a read only repository or not and exposes 2 different methods to return the given type of interface (meaning you need to know the type at compile time).
public static class RepositoryFactory
{
public static bool ConnectionStringIsReadOnly(string connectionString)
{
return connectionString.Contains("user=hacker");
}
public static IRead GetReadOnlyRepository(string connectionString)
{
return new Repository(connectionString);
}
public static IRepository GetRepository(string connectionString)
{
if (ConnectionStringIsReadOnly(connectionString)) throw new ArgumentException(@"Given connectionString is not allowed full repository access", "connectionString");
return new Repository(connectionString);
}
}
You can then consume this as follows, seperating the points where you try to do actions that require full access by checking that you have a version of IRepository or of IRead only.
public class Program
{
public void ConsumeRepository(string connectionString)
{
IRead reader = null;
IRepository repository = null;
if (RepositoryFactory.ConnectionStringIsReadOnly(connectionString))
reader = RepositoryFactory.GetReadOnlyRepository(connectionString);
else
{
repository = RepositoryFactory.GetRepository(connectionString);
reader = repository;
}
object o = reader.Read();
// do something with o
// if allowed then write o to repository
if (repository != null)
{
repository.Write(o);
}
}
}
精彩评论